diff --git a/sql/base/auth_database.sql b/sql/base/auth_database.sql index 14feed913a4..275da0fd910 100644 --- a/sql/base/auth_database.sql +++ b/sql/base/auth_database.sql @@ -180,7 +180,6 @@ CREATE TABLE `battlenet_account_bans` ( `unbandate` int(10) unsigned NOT NULL DEFAULT '0', `bannedby` varchar(50) NOT NULL, `banreason` varchar(255) NOT NULL, - `active` tinyint(3) unsigned NOT NULL DEFAULT '1', PRIMARY KEY (`id`,`bandate`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Ban List'; /*!40101 SET character_set_client = @saved_cs_client */; diff --git a/sql/updates/auth/2016_01_13_00_auth_434.sql b/sql/updates/auth/2016_01_13_00_auth_434.sql new file mode 100644 index 00000000000..3d205b86298 --- /dev/null +++ b/sql/updates/auth/2016_01_13_00_auth_434.sql @@ -0,0 +1 @@ +ALTER TABLE `battlenet_account_bans` DROP COLUMN `active`; diff --git a/src/server/bnetserver/Server/Session.cpp b/src/server/bnetserver/Server/Session.cpp index 36e5e19f321..2fed7fc3572 100644 --- a/src/server/bnetserver/Server/Session.cpp +++ b/src/server/bnetserver/Server/Session.cpp @@ -36,8 +36,37 @@ Battlenet::Session::ModuleHandler const Battlenet::Session::ModuleHandlers[MODUL &Battlenet::Session::HandleResumeModule, }; -Battlenet::Session::Session(tcp::socket&& socket) : Socket(std::move(socket)), _accountId(0), _accountName(), _locale(), - _os(), _build(0), _gameAccountId(0), _gameAccountName(), _accountSecurityLevel(SEC_PLAYER), I(), s(), v(), b(), B(), K(), +void Battlenet::AccountInfo::LoadResult(Field* fields) +{ + // ba.id, ba.email, ba.locked, ba.lock_country, ba.last_ip, ba.failed_logins, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, bab.unbandate = bab.bandate FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab WHERE email = ? + Id = fields[0].GetUInt32(); + Login = fields[1].GetString(); + IsLockedToIP = fields[2].GetBool(); + LockCountry = fields[3].GetString(); + LastIP = fields[4].GetString(); + FailedLogins = fields[5].GetUInt32(); + IsBanned = fields[6].GetUInt64() != 0; + IsPermanentlyBanned = fields[7].GetUInt64() != 0; +} + +void Battlenet::GameAccountInfo::LoadResult(Field* fields) +{ + // a.id, a.username, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, ab.unbandate = ab.bandate, aa.gmlevel + Id = fields[0].GetUInt32(); + Name = fields[1].GetString(); + IsBanned = fields[2].GetUInt64() != 0; + IsPermanentlyBanned = fields[3].GetUInt64() != 0; + SecurityLevel = AccountTypes(fields[4].GetUInt8()); + + std::size_t hashPos = Name.find('#'); + if (hashPos != std::string::npos) + DisplayName = std::string("WoW") + Name.substr(hashPos + 1); + else + DisplayName = Name; +} + +Battlenet::Session::Session(tcp::socket&& socket) : Socket(std::move(socket)), _accountInfo(new AccountInfo()), _gameAccountInfo(nullptr), _locale(), + _os(), _build(0), _ipCountry(), I(), s(), v(), b(), B(), K(), _reconnectProof(), _crypt(), _authed(false), _subscribedToRealmListUpdates(false) { static uint8 const N_Bytes[] = @@ -63,7 +92,10 @@ Battlenet::Session::Session(tcp::socket&& socket) : Socket(std::move(socket)), _ Battlenet::Session::~Session() { - sSessionMgr.RemoveSession(this); + if (_authed) + sSessionMgr.RemoveSession(this); + + delete _accountInfo; } void Battlenet::Session::_SetVSFields(std::string const& pstr) @@ -83,7 +115,7 @@ void Battlenet::Session::_SetVSFields(std::string const& pstr) PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_VS_FIELDS); stmt->setString(0, v.AsHexStr()); stmt->setString(1, s.AsHexStr()); - stmt->setString(2, _accountName); + stmt->setString(2, _accountInfo->Login); LoginDatabase.Execute(stmt); } @@ -95,18 +127,12 @@ void Battlenet::Session::LogUnhandledPacket(PacketHeader const& header) void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const& logonRequest) { - // Verify that this IP is not in the ip_banned table - LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS)); - - std::string ip_address = GetRemoteIpAddress().to_string(); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_BANNED); - stmt->setString(0, ip_address); - if (PreparedQueryResult result = LoginDatabase.Query(stmt)) + if (_queryCallback) { Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); - logonResponse->SetAuthResult(LOGIN_BANNED); + logonResponse->SetAuthResult(AUTH_LOGON_TOO_FAST); AsyncWrite(logonResponse); - TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] Banned ip '%s:%d' tries to login!", ip_address.c_str(), GetRemotePort()); + TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s attempted to log too quick after previous attempt!", GetClientInfo().c_str()); return; } @@ -159,15 +185,20 @@ void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const& _build = component.Build; } - _accountName = logonRequest.Login; + std::string login = logonRequest.Login; _locale = logonRequest.Locale; _os = logonRequest.Platform; - Utf8ToUpperOnlyLatin(_accountName); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ACCOUNT_INFO); - stmt->setString(0, _accountName); + Utf8ToUpperOnlyLatin(login); + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ACCOUNT_INFO); + stmt->setString(0, login); - PreparedQueryResult result = LoginDatabase.Query(stmt); + _queryCallback = std::bind(&Battlenet::Session::HandleLogonRequestCallback, this, std::placeholders::_1); + _queryFuture = LoginDatabase.AsyncQuery(stmt); +} + +void Battlenet::Session::HandleLogonRequestCallback(PreparedQueryResult result) +{ if (!result) { Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); @@ -177,15 +208,27 @@ void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const& } Field* fields = result->Fetch(); + _accountInfo->LoadResult(fields); - _accountId = fields[1].GetUInt32(); + std::string pStr = fields[8].GetString(); + std::string databaseV = fields[9].GetString(); + std::string databaseS = fields[10].GetString(); - // If the IP is 'locked', check that the player comes indeed from the correct IP address - if (fields[2].GetUInt8() == 1) // if ip is locked + _gameAccounts.resize(result->GetRowCount()); + uint32 i = 0; + do { - TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] Account '%s' is locked to IP - '%s' is logging in from '%s'", _accountName.c_str(), fields[4].GetCString(), ip_address.c_str()); + _gameAccounts[i++].LoadResult(result->Fetch() + 11); - if (strcmp(fields[4].GetCString(), ip_address.c_str()) != 0) + } while (result->NextRow()); + + std::string ip_address = GetRemoteIpAddress().to_string(); + // If the IP is 'locked', check that the player comes indeed from the correct IP address + if (_accountInfo->IsLockedToIP) // if ip is locked + { + TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] Account '%s' is locked to IP - '%s' is logging in from '%s'", _accountInfo->Login.c_str(), _accountInfo->LastIP.c_str(), ip_address.c_str()); + + if (_accountInfo->LastIP != ip_address) { Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); logonResponse->SetAuthResult(AUTH_ACCOUNT_LOCKED); @@ -195,48 +238,31 @@ void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const& } else { - TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] Account '%s' is not locked to ip", _accountName.c_str()); - std::string accountCountry = fields[3].GetString(); - if (accountCountry.empty() || accountCountry == "00") - TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] Account '%s' is not locked to country", _accountName.c_str()); - else if (!accountCountry.empty()) + TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] Account '%s' is not locked to ip", _accountInfo->Login.c_str()); + if (_accountInfo->LockCountry.empty() || _accountInfo->LockCountry == "00") + TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] Account '%s' is not locked to country", _accountInfo->Login.c_str()); + else if (!_accountInfo->LockCountry.empty() && !_ipCountry.empty()) { - uint32 ip = inet_addr(ip_address.c_str()); - EndianConvertReverse(ip); - - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGON_COUNTRY); - stmt->setUInt32(0, ip); - if (PreparedQueryResult sessionCountryQuery = LoginDatabase.Query(stmt)) + TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] Account '%s' is locked to country: '%s' Player country is '%s'", _accountInfo->Login.c_str(), _accountInfo->LockCountry.c_str(), _ipCountry.c_str()); + if (_ipCountry != _accountInfo->LockCountry) { - std::string loginCountry = (*sessionCountryQuery)[0].GetString(); - TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] Account '%s' is locked to country: '%s' Player country is '%s'", _accountName.c_str(), accountCountry.c_str(), loginCountry.c_str()); - if (loginCountry != accountCountry) - { - Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); - logonResponse->SetAuthResult(AUTH_ACCOUNT_LOCKED); - AsyncWrite(logonResponse); - return; - } + Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); + logonResponse->SetAuthResult(AUTH_ACCOUNT_LOCKED); + AsyncWrite(logonResponse); + return; } } } - //set expired bans to inactive - LoginDatabase.DirectExecute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_BNET_EXPIRED_BANS)); - // If the account is banned, reject the logon attempt - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ACTIVE_ACCOUNT_BAN); - stmt->setUInt32(0, _accountId); - PreparedQueryResult banresult = LoginDatabase.Query(stmt); - if (banresult) + if (_accountInfo->IsBanned) { - Field* fields = banresult->Fetch(); - if (fields[0].GetUInt32() == fields[1].GetUInt32()) + if (_accountInfo->IsPermanentlyBanned) { Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); logonResponse->SetAuthResult(LOGIN_BANNED); AsyncWrite(logonResponse); - TC_LOG_DEBUG("session", "'%s:%d' [Battlenet::LogonRequest] Banned account %s tried to login!", ip_address.c_str(), GetRemotePort(), _accountName.c_str()); + TC_LOG_DEBUG("session", "'%s:%d' [Battlenet::LogonRequest] Banned account %s tried to login!", ip_address.c_str(), GetRemotePort(), _accountInfo->Login.c_str()); return; } else @@ -244,13 +270,13 @@ void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const& Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); logonResponse->SetAuthResult(LOGIN_SUSPENDED); AsyncWrite(logonResponse); - TC_LOG_DEBUG("session", "'%s:%d' [Battlenet::LogonRequest] Temporarily banned account %s tried to login!", ip_address.c_str(), GetRemotePort(), _accountName.c_str()); + TC_LOG_DEBUG("session", "'%s:%d' [Battlenet::LogonRequest] Temporarily banned account %s tried to login!", ip_address.c_str(), GetRemotePort(), _accountInfo->Login.c_str()); return; } } SHA256Hash sha; - sha.UpdateData(_accountName); + sha.UpdateData(_accountInfo->Login); sha.Finalize(); I.SetBinary(sha.GetDigest(), sha.GetLength()); @@ -258,11 +284,6 @@ void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const& ModuleInfo* password = sModuleMgr->CreateModule(_os, "Password"); ModuleInfo* thumbprint = sModuleMgr->CreateModule(_os, "Thumbprint"); - std::string pStr = fields[0].GetString(); - - std::string databaseV = fields[5].GetString(); - std::string databaseS = fields[6].GetString(); - if (databaseV.size() != size_t(BufferSizes::SRP_6_V) * 2 || databaseS.size() != size_t(BufferSizes::SRP_6_S) * 2) _SetVSFields(pStr); else @@ -299,18 +320,33 @@ void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const& void Battlenet::Session::HandleResumeRequest(Authentication::ResumeRequest const& resumeRequest) { - _accountName = resumeRequest.Login; + if (_queryCallback) + { + Authentication::ResumeResponse* logonResponse = new Authentication::ResumeResponse(); + logonResponse->SetAuthResult(AUTH_LOGON_TOO_FAST); + AsyncWrite(logonResponse); + TC_LOG_DEBUG("session", "[Battlenet::ResumeRequest] %s attempted to log too quick after previous attempt!", GetClientInfo().c_str()); + return; + } + + std::string login = resumeRequest.Login; _locale = resumeRequest.Locale; _os = resumeRequest.Platform; auto baseComponent = std::find_if(resumeRequest.Components.begin(), resumeRequest.Components.end(), [](Component const& c) { return c.Program == "base"; }); if (baseComponent != resumeRequest.Components.end()) _build = baseComponent->Build; - Utf8ToUpperOnlyLatin(_accountName); + Utf8ToUpperOnlyLatin(login); PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_RECONNECT_INFO); - stmt->setString(0, _accountName); + stmt->setString(0, login); stmt->setString(1, resumeRequest.GameAccountName); - PreparedQueryResult result = LoginDatabase.Query(stmt); + + _queryCallback = std::bind(&Battlenet::Session::HandleResumeRequestCallback, this, std::placeholders::_1); + _queryFuture = LoginDatabase.AsyncQuery(stmt); +} + +void Battlenet::Session::HandleResumeRequestCallback(PreparedQueryResult result) +{ if (!result) { Authentication::ResumeResponse* resumeResponse = new Authentication::ResumeResponse(); @@ -320,11 +356,12 @@ void Battlenet::Session::HandleResumeRequest(Authentication::ResumeRequest const } Field* fields = result->Fetch(); + _accountInfo->LoadResult(fields); + K.SetHexStr(fields[8].GetString().c_str()); - _accountId = fields[0].GetUInt32(); - K.SetHexStr(fields[1].GetString().c_str()); - _gameAccountId = fields[2].GetUInt32(); - _gameAccountName = resumeRequest.GameAccountName; + _gameAccounts.resize(1); + _gameAccountInfo = &_gameAccounts[0]; + _gameAccountInfo->LoadResult(fields + 9); ModuleInfo* thumbprint = sModuleMgr->CreateModule(_os, "Thumbprint"); ModuleInfo* resume = sModuleMgr->CreateModule(_os, "Resume"); @@ -373,7 +410,6 @@ void Battlenet::Session::HandleProofResponse(Authentication::ProofResponse const } AsyncWrite(response); - return; } void Battlenet::Session::HandlePing(Connection::Ping const& /*ping*/) @@ -391,7 +427,7 @@ void Battlenet::Session::HandleLogoutRequest(Connection::LogoutRequest const& /* PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_SESSION_KEY); stmt->setString(0, ""); stmt->setBool(1, false); - stmt->setUInt32(2, _accountId); + stmt->setUInt32(2, _accountInfo->Id); LoginDatabase.Execute(stmt); } @@ -401,19 +437,28 @@ void Battlenet::Session::HandleConnectionClosing(Connection::ConnectionClosing c void Battlenet::Session::HandleListSubscribeRequest(WoWRealm::ListSubscribeRequest const& /*listSubscribeRequest*/) { - WoWRealm::ListSubscribeResponse* listSubscribeResponse = new WoWRealm::ListSubscribeResponse(); + if (_subscribedToRealmListUpdates || _queryCallback) + return; PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS); - stmt->setUInt32(0, _gameAccountId); + stmt->setUInt32(0, _gameAccountInfo->Id); - if (PreparedQueryResult countResult = LoginDatabase.Query(stmt)) + _queryCallback = std::bind(&Battlenet::Session::HandleListSubscribeRequestCallback, this, std::placeholders::_1); + _queryFuture = LoginDatabase.AsyncQuery(stmt); +} + +void Battlenet::Session::HandleListSubscribeRequestCallback(PreparedQueryResult result) +{ + WoWRealm::ListSubscribeResponse* listSubscribeResponse = new WoWRealm::ListSubscribeResponse(); + + if (result) { do { - Field* fields = countResult->Fetch(); + Field* fields = result->Fetch(); uint32 build = fields[4].GetUInt32(); listSubscribeResponse->CharacterCounts.push_back({ RealmId(fields[2].GetUInt8(), fields[3].GetUInt8(), fields[1].GetUInt32(), (_build != build ? build : 0)), fields[0].GetUInt8() }); - } while (countResult->NextRow()); + } while (result->NextRow()); } for (RealmList::RealmMap::value_type const& i : sRealmList->GetRealms()) @@ -461,7 +506,7 @@ void Battlenet::Session::HandleJoinRequestV2(WoWRealm::JoinRequestV2 const& join memcpy(sessionKey + hmac.GetLength(), hmac2.GetDigest(), hmac2.GetLength()); LoginDatabase.DirectPExecute("UPDATE account SET sessionkey = '%s', last_ip = '%s', last_login = NOW(), locale = %u, failed_logins = 0, os = '%s' WHERE id = %u", - ByteArrayToHexStr(sessionKey, 40, true).c_str(), GetRemoteIpAddress().to_string().c_str(), GetLocaleByName(_locale), _os.c_str(), _gameAccountId); + ByteArrayToHexStr(sessionKey, 40, true).c_str(), GetRemoteIpAddress().to_string().c_str(), GetLocaleByName(_locale), _os.c_str(), _gameAccountInfo->Id); joinResponse->IPv4.emplace_back(realm->ExternalAddress, realm->Port); if (realm->ExternalAddress != realm->LocalAddress) @@ -560,10 +605,73 @@ void Battlenet::Session::ReadHandler() void Battlenet::Session::Start() { - TC_LOG_TRACE("session", "Accepted connection from %s", GetRemoteIpAddress().to_string().c_str()); + std::string ip_address = GetRemoteIpAddress().to_string(); + TC_LOG_TRACE("session", "Accepted connection from %s", ip_address.c_str()); + + if (_queryCallback) + { + Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); + logonResponse->SetAuthResult(AUTH_LOGON_TOO_FAST); + AsyncWrite(logonResponse); + TC_LOG_DEBUG("session", "[Session::Start] %s attempted to log too quick after previous attempt!", GetClientInfo().c_str()); + return; + } + + // Verify that this IP is not in the ip_banned table + LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS)); + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO); + stmt->setString(0, ip_address); + stmt->setUInt32(1, inet_addr(ip_address.c_str())); + + _queryCallback = std::bind(&Battlenet::Session::CheckIpCallback, this, std::placeholders::_1); + _queryFuture = LoginDatabase.AsyncQuery(stmt); +} + +void Battlenet::Session::CheckIpCallback(PreparedQueryResult result) +{ + if (result) + { + bool banned = false; + do + { + Field* fields = result->Fetch(); + if (fields[0].GetUInt64() != 0) + banned = true; + + if (!fields[1].GetString().empty()) + _ipCountry = fields[1].GetString(); + + } while (result->NextRow()); + + if (banned) + { + Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); + logonResponse->SetAuthResult(AUTH_INTERNAL_ERROR); + AsyncWrite(logonResponse); + TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] Banned ip '%s:%d' tries to login!", GetRemoteIpAddress().to_string().c_str(), GetRemotePort()); + return; + } + } + AsyncRead(); } +bool Battlenet::Session::Update() +{ + if (!BattlenetSocket::Update()) + return false; + + if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) + { + auto callback = std::move(_queryCallback); + _queryCallback = nullptr; + callback(_queryFuture.get()); + } + + return true; +} + void Battlenet::Session::AsyncWrite(ServerPacket* packet) { if (!IsOpen()) @@ -613,7 +721,6 @@ bool Battlenet::Session::HandlePasswordModule(BitStream* dataStream, ServerPacke return false; } - BigNumber A, clientM1, clientChallenge; A.SetBinary(dataStream->ReadBytes(128).get(), 128); clientM1.SetBinary(dataStream->ReadBytes(32).get(), 32); @@ -694,7 +801,7 @@ bool Battlenet::Session::HandlePasswordModule(BitStream* dataStream, ServerPacke if (memcmp(M1.AsByteArray().get(), clientM1.AsByteArray().get(), 32)) { PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_FAILED_LOGINS); - stmt->setString(0, _accountName); + stmt->setString(0, _accountInfo->Login); LoginDatabase.Execute(stmt); Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); @@ -703,14 +810,7 @@ bool Battlenet::Session::HandlePasswordModule(BitStream* dataStream, ServerPacke return false; } - uint64 numAccounts = 0; - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_GAME_ACCOUNTS); - stmt->setUInt32(0, _accountId); - PreparedQueryResult result = LoginDatabase.Query(stmt); - if (result) - numAccounts = result->GetRowCount(); - - if (!numAccounts) + if (_gameAccounts.empty()) { Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); logonResponse->SetAuthResult(LOGIN_NO_GAME_ACCOUNT); @@ -718,10 +818,8 @@ bool Battlenet::Session::HandlePasswordModule(BitStream* dataStream, ServerPacke return false; } - Field* fields = result->Fetch(); - //set expired game account bans to inactive - LoginDatabase.DirectExecute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS)); + LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS)); BigNumber M; sha.Initialize(); @@ -746,25 +844,17 @@ bool Battlenet::Session::HandlePasswordModule(BitStream* dataStream, ServerPacke Authentication::ProofRequest* proofRequest = new Authentication::ProofRequest(); proofRequest->Modules.push_back(password); - if (numAccounts > 1) + if (_gameAccounts.size() > 1) { BitStream accounts; state = 0; accounts.WriteBytes(&state, 1); - accounts.Write(numAccounts, 8); - do + accounts.Write(_gameAccounts.size(), 8); + for (GameAccountInfo const& gameAccount : _gameAccounts) { - fields = result->Fetch(); - std::ostringstream name; - std::string originalName = fields[1].GetString(); - if (originalName.find('#') != std::string::npos) - name << "WoW" << uint32(fields[0].GetUInt8()); - else - name << originalName; - accounts.Write(2, 8); - accounts.WriteString(name.str(), 8); - } while (result->NextRow()); + accounts.WriteString(gameAccount.DisplayName, 8); + } ModuleInfo* selectGameAccount = sModuleMgr->CreateModule(_os, "SelectGameAccount"); selectGameAccount->DataSize = accounts.GetSize(); @@ -775,29 +865,28 @@ bool Battlenet::Session::HandlePasswordModule(BitStream* dataStream, ServerPacke } else { - if (fields[4].GetBool()) + _gameAccountInfo = &_gameAccounts[0]; + + if (_gameAccountInfo->IsBanned) { delete proofRequest; Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); - if (fields[2].GetUInt32() == fields[3].GetUInt32()) + if (_gameAccountInfo->IsPermanentlyBanned) { logonResponse->SetAuthResult(LOGIN_BANNED); - TC_LOG_DEBUG("session", "'%s:%d' [Battlenet::Password] Banned account %s tried to login!", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountName.c_str()); + TC_LOG_DEBUG("session", "'%s:%d' [Battlenet::Password] Banned account %s tried to login!", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo->Login.c_str()); } else { logonResponse->SetAuthResult(LOGIN_SUSPENDED); - TC_LOG_DEBUG("session", "'%s:%d' [Battlenet::Password] Temporarily banned account %s tried to login!", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountName.c_str()); + TC_LOG_DEBUG("session", "'%s:%d' [Battlenet::Password] Temporarily banned account %s tried to login!", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo->Login.c_str()); } ReplaceResponse(response, logonResponse); return false; } - _gameAccountId = fields[0].GetUInt32(); - _gameAccountName = fields[1].GetString(); - proofRequest->Modules.push_back(sModuleMgr->CreateModule(_os, "RiskFingerprint")); _modulesWaitingForData.push(MODULE_RISK_FINGERPRINT); } @@ -826,21 +915,16 @@ bool Battlenet::Session::HandleSelectGameAccountModule(BitStream* dataStream, Se return false; } - PreparedStatement* stmt; - if (account.substr(0, 3) != "WoW") + for (std::size_t i = 0; i < _gameAccounts.size(); ++i) { - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_GAME_ACCOUNT); - stmt->setString(0, account); - } - else - { - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_GAME_ACCOUNT_UNNAMED); - stmt->setUInt8(0, atol(account.substr(3).c_str())); + if (_gameAccounts[i].DisplayName == account) + { + _gameAccountInfo = &_gameAccounts[i]; + break; + } } - stmt->setUInt32(1, _accountId); - PreparedQueryResult result = LoginDatabase.Query(stmt); - if (!result) + if (!_gameAccountInfo) { Authentication::LogonResponse* complete = new Authentication::LogonResponse(); complete->SetAuthResult(LOGIN_NO_GAME_ACCOUNT); @@ -848,28 +932,24 @@ bool Battlenet::Session::HandleSelectGameAccountModule(BitStream* dataStream, Se return false; } - Field* fields = result->Fetch(); - if (fields[4].GetBool()) + if (_gameAccountInfo->IsBanned) { Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); - if (fields[2].GetUInt32() == fields[3].GetUInt32()) + if (_gameAccountInfo->IsPermanentlyBanned) { logonResponse->SetAuthResult(LOGIN_BANNED); - TC_LOG_DEBUG("session", "'%s:%d' [Battlenet::SelectGameAccount] Banned account %s tried to login!", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountName.c_str()); + TC_LOG_DEBUG("session", "'%s:%d' [Battlenet::SelectGameAccount] Banned account %s tried to login!", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo->Login.c_str()); } else { logonResponse->SetAuthResult(LOGIN_SUSPENDED); - TC_LOG_DEBUG("session", "'%s:%d' [Battlenet::SelectGameAccount] Temporarily banned account %s tried to login!", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountName.c_str()); + TC_LOG_DEBUG("session", "'%s:%d' [Battlenet::SelectGameAccount] Temporarily banned account %s tried to login!", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo->Login.c_str()); } ReplaceResponse(response, logonResponse); return false; } - _gameAccountId = fields[0].GetUInt32(); - _gameAccountName = fields[1].GetString(); - Authentication::ProofRequest* proofRequest = new Authentication::ProofRequest(); proofRequest->Modules.push_back(sModuleMgr->CreateModule(_os, "RiskFingerprint")); ReplaceResponse(response, proofRequest); @@ -881,29 +961,26 @@ bool Battlenet::Session::HandleSelectGameAccountModule(BitStream* dataStream, Se bool Battlenet::Session::HandleRiskFingerprintModule(BitStream* dataStream, ServerPacket** response) { Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); - if (dataStream->Read(8) == 1) + if (dataStream->Read(8) == 1 && _accountInfo && _gameAccountInfo) { - logonResponse->AccountId = _accountId; - logonResponse->GameAccountName = _gameAccountName; + logonResponse->AccountId = _accountInfo->Id; + logonResponse->GameAccountName = _gameAccountInfo->Name; logonResponse->GameAccountFlags = GAMEACCOUNT_FLAG_PROPASS_LOCK; - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_FAILED_LOGINS); - stmt->setUInt32(0, _accountId); - if (PreparedQueryResult failedLoginsResult = LoginDatabase.Query(stmt)) - logonResponse->FailedLogins = (*failedLoginsResult)[0].GetUInt32(); + logonResponse->FailedLogins = _accountInfo->FailedLogins; SQLTransaction trans = LoginDatabase.BeginTransaction(); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_LAST_LOGIN_INFO); + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_LAST_LOGIN_INFO); stmt->setString(0, GetRemoteIpAddress().to_string()); stmt->setUInt8(1, GetLocaleByName(_locale)); stmt->setString(2, _os); - stmt->setUInt32(3, _accountId); + stmt->setUInt32(3, _accountInfo->Id); trans->Append(stmt); stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_SESSION_KEY); stmt->setString(0, K.AsHexStr()); stmt->setBool(1, true); - stmt->setUInt32(2, _accountId); + stmt->setUInt32(2, _accountInfo->Id); trans->Append(stmt); LoginDatabase.CommitTransaction(trans); @@ -963,7 +1040,7 @@ bool Battlenet::Session::HandleResumeModule(BitStream* dataStream, ServerPacket* if (memcmp(proof.GetDigest(), clientProof.get(), serverPart.GetLength())) { PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_FAILED_LOGINS); - stmt->setString(0, _accountName); + stmt->setString(0, _accountInfo->Login); LoginDatabase.Execute(stmt); TC_LOG_DEBUG("session", "[Battlenet::Resume] Invalid proof!"); @@ -976,7 +1053,7 @@ bool Battlenet::Session::HandleResumeModule(BitStream* dataStream, ServerPacket* PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_SESSION_KEY); stmt->setString(0, K.AsHexStr()); stmt->setBool(1, true); - stmt->setUInt32(2, _accountId); + stmt->setUInt32(2, _accountInfo->Id); LoginDatabase.Execute(stmt); HmacSha256 serverProof(64, newSessionKey); @@ -1041,7 +1118,7 @@ Battlenet::WoWRealm::ListUpdate* Battlenet::Session::BuildListUpdate(Realm const WoWRealm::ListUpdate* listUpdate = new WoWRealm::ListUpdate(); listUpdate->Timezone = realm->Timezone; listUpdate->Population = realm->PopulationLevel; - listUpdate->Lock = (realm->AllowedSecurityLevel > _accountSecurityLevel) ? 1 : 0; + listUpdate->Lock = (realm->AllowedSecurityLevel > _gameAccountInfo->SecurityLevel) ? 1 : 0; listUpdate->Type = realm->Type; listUpdate->Name = realm->Name; @@ -1063,11 +1140,11 @@ std::string Battlenet::Session::GetClientInfo() const { std::ostringstream stream; stream << '[' << GetRemoteIpAddress() << ':' << GetRemotePort(); - if (!_accountName.empty()) - stream << ", Account: " << _accountName; + if (_accountInfo && !_accountInfo->Login.empty()) + stream << ", Account: " << _accountInfo->Login; - if (!_gameAccountName.empty()) - stream << ", Game account: " << _gameAccountName; + if (_gameAccountInfo) + stream << ", Game account: " << _gameAccountInfo->Name; stream << ']'; diff --git a/src/server/bnetserver/Server/Session.h b/src/server/bnetserver/Server/Session.h index bcad7ba888e..650f987256a 100644 --- a/src/server/bnetserver/Server/Session.h +++ b/src/server/bnetserver/Server/Session.h @@ -52,6 +52,32 @@ namespace Battlenet Read = 0x4000 }; + struct AccountInfo + { + void LoadResult(Field* fields); + + uint32 Id; + std::string Login; + bool IsLockedToIP; + std::string LockCountry; + std::string LastIP; + uint32 FailedLogins; + bool IsBanned; + bool IsPermanentlyBanned; + }; + + struct GameAccountInfo + { + void LoadResult(Field* fields); + + uint32 Id; + std::string Name; + std::string DisplayName; + bool IsBanned; + bool IsPermanentlyBanned; + AccountTypes SecurityLevel; + }; + class Session : public Socket { typedef Socket BattlenetSocket; @@ -85,11 +111,12 @@ namespace Battlenet void HandleGetStreamItemsRequest(Cache::GetStreamItemsRequest const& getStreamItemsRequest); void Start() override; + bool Update() override; void UpdateRealms(std::vector& realms, std::vector& deletedRealms); - uint32 GetAccountId() const { return _accountId; } - uint32 GetGameAccountId() const { return _gameAccountId; } + uint32 GetAccountId() const { return _accountInfo->Id; } + uint32 GetGameAccountId() const { return _gameAccountInfo->Id; } bool IsSubscribedToRealmListUpdates() const { return _subscribedToRealmListUpdates; } @@ -104,6 +131,11 @@ namespace Battlenet typedef bool(Session::*ModuleHandler)(BitStream* dataStream, ServerPacket** response); static ModuleHandler const ModuleHandlers[MODULE_COUNT]; + void CheckIpCallback(PreparedQueryResult result); + void HandleLogonRequestCallback(PreparedQueryResult result); + void HandleResumeRequestCallback(PreparedQueryResult result); + void HandleListSubscribeRequestCallback(PreparedQueryResult result); + bool HandlePasswordModule(BitStream* dataStream, ServerPacket** response); bool HandleSelectGameAccountModule(BitStream* dataStream, ServerPacket** response); bool HandleRiskFingerprintModule(BitStream* dataStream, ServerPacket** response); @@ -113,14 +145,15 @@ namespace Battlenet WoWRealm::ListUpdate* BuildListUpdate(Realm const* realm) const; std::string GetClientInfo() const; - uint32 _accountId; - std::string _accountName; + AccountInfo* _accountInfo; + GameAccountInfo* _gameAccountInfo; // Points at selected game account (inside _gameAccounts) + std::vector _gameAccounts; + std::string _locale; std::string _os; uint32 _build; - uint32 _gameAccountId; - std::string _gameAccountName; - AccountTypes _accountSecurityLevel; + + std::string _ipCountry; BigNumber N; BigNumber g; @@ -141,6 +174,9 @@ namespace Battlenet PacketCrypt _crypt; bool _authed; bool _subscribedToRealmListUpdates; + + PreparedQueryResultFuture _queryFuture; + std::function _queryCallback; }; } diff --git a/src/server/bnetserver/Server/SessionManager.cpp b/src/server/bnetserver/Server/SessionManager.cpp index 3e7cba68a4f..b0e2ceca508 100644 --- a/src/server/bnetserver/Server/SessionManager.cpp +++ b/src/server/bnetserver/Server/SessionManager.cpp @@ -52,6 +52,7 @@ void Battlenet::SessionManager::RemoveSession(Session* session) Battlenet::Session* Battlenet::SessionManager::GetSession(uint32 accountId, uint32 gameAccountId) const { + boost::shared_lock lock(_sessionMutex); auto itr = _sessions.find({ accountId, gameAccountId }); if (itr != _sessions.end()) return itr->second; @@ -61,6 +62,7 @@ Battlenet::Session* Battlenet::SessionManager::GetSession(uint32 accountId, uint std::list Battlenet::SessionManager::GetSessions(uint32 accountId) const { + boost::shared_lock lock(_sessionMutex); std::list sessions; auto itr = _sessionsByAccountId.find(accountId); if (itr != _sessionsByAccountId.end()) diff --git a/src/server/bnetserver/Server/SessionManager.h b/src/server/bnetserver/Server/SessionManager.h index d8f5f5af92f..014d5a4bd98 100644 --- a/src/server/bnetserver/Server/SessionManager.h +++ b/src/server/bnetserver/Server/SessionManager.h @@ -64,7 +64,7 @@ namespace Battlenet std::list GetSessions(uint32 accountId) const; template - void LockedForEach(Iterator iterator) + void LockedForEach(Iterator iterator) const { boost::shared_lock lock(_sessionMutex); for (SessionMap::value_type const& pair : _sessions) @@ -79,7 +79,7 @@ namespace Battlenet SessionMap _sessions; SessionByAccountMap _sessionsByAccountId; - boost::shared_mutex _sessionMutex; + mutable boost::shared_mutex _sessionMutex; }; } diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 964ea7dbb7a..0a4c288d559 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -267,7 +267,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_REP_PLAYER_CURRENCY, "REPLACE INTO character_currency (guid, currency, week_count, total_count) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); // Account data - PrepareStatement(CHAR_SEL_ACCOUNT_DATA, "SELECT type, time, data FROM account_data WHERE accountId = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_ACCOUNT_DATA, "SELECT type, time, data FROM account_data WHERE accountId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_REP_ACCOUNT_DATA, "REPLACE INTO account_data (accountId, type, time, data) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ACCOUNT_DATA, "DELETE FROM account_data WHERE accountId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_PLAYER_ACCOUNT_DATA, "SELECT type, time, data FROM character_account_data WHERE guid = ?", CONNECTION_ASYNC); @@ -275,7 +275,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_PLAYER_ACCOUNT_DATA, "DELETE FROM character_account_data WHERE guid = ?", CONNECTION_ASYNC); // Tutorials - PrepareStatement(CHAR_SEL_TUTORIALS, "SELECT tut0, tut1, tut2, tut3, tut4, tut5, tut6, tut7 FROM account_tutorial WHERE accountId = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_TUTORIALS, "SELECT tut0, tut1, tut2, tut3, tut4, tut5, tut6, tut7 FROM account_tutorial WHERE accountId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_HAS_TUTORIALS, "SELECT 1 FROM account_tutorial WHERE accountId = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_INS_TUTORIALS, "INSERT INTO account_tutorial(tut0, tut1, tut2, tut3, tut4, tut5, tut6, tut7, accountId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_TUTORIALS, "UPDATE account_tutorial SET tut0 = ?, tut1 = ?, tut2 = ?, tut3 = ?, tut4 = ?, tut5 = ?, tut6 = ?, tut7 = ? WHERE accountId = ?", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 029cbbc0c60..270d68693e3 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -26,6 +26,9 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_DEL_EXPIRED_IP_BANS, "DELETE FROM ip_banned WHERE unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS, "UPDATE account_banned SET active = 0 WHERE active = 1 AND unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_IP_BANNED, "SELECT * FROM ip_banned WHERE ip = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_IP_INFO, "(SELECT unbandate > UNIX_TIMESTAMP() OR unbandate = bandate AS banned, NULL as country FROM ip_banned WHERE ip = ?) " + "UNION " + "(SELECT NULL AS banned, country FROM ip2nation WHERE INET_NTOA(ip) = ?)", CONNECTION_ASYNC); PrepareStatement(LOGIN_INS_IP_AUTO_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity Auth', 'Failed login autoban')", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_IP_BANNED_ALL, "SELECT ip, bandate, unbandate, bannedby, banreason FROM ip_banned WHERE (bandate = unbandate OR unbandate > UNIX_TIMESTAMP()) ORDER BY unbandate", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_IP_BANNED_BY_IP, "SELECT ip, bandate, unbandate, bannedby, banreason FROM ip_banned WHERE (bandate = unbandate OR unbandate > UNIX_TIMESTAMP()) AND ip LIKE CONCAT('%%', ?, '%%') ORDER BY unbandate", CONNECTION_SYNCH); @@ -43,7 +46,12 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_FAILEDLOGINS, "SELECT id, failed_logins FROM account WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME, "SELECT id FROM account WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME, "SELECT id, username FROM account WHERE username = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os, battlenet_account FROM account WHERE username = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, ba.locale, a.recruiter, ba.os, ba.id, aa.gmLevel, " + "bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id " + "FROM account a LEFT JOIN account r ON a.id = r.recruiter LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id " + "LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID IN (-1, ?) LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 " + "WHERE a.username = ? ORDER BY aa.RealmID DESC LIMIT 1", CONNECTION_ASYNC); + PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL, "SELECT id, username FROM account WHERE email = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_NUM_CHARS_ON_REALM, "SELECT numchars FROM realmcharacters WHERE realmid = ? AND acctid= ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_BY_IP, "SELECT id, username FROM account WHERE last_ip = ?", CONNECTION_SYNCH); @@ -88,8 +96,6 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_ACCOUNT_INFO, "SELECT a.username, a.last_ip, aa.gmlevel, a.expansion FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS_GMLEVEL_TEST, "SELECT 1 FROM account_access WHERE id = ? AND gmlevel > ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS, "SELECT a.id, aa.gmlevel, aa.RealmID FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.username = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_ACCOUNT_RECRUITER, "SELECT 1 FROM account WHERE recruiter = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_BANS, "SELECT 1 FROM account_banned WHERE id = ? AND active = 1 UNION SELECT 1 FROM ip_banned WHERE ip = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_WHOIS, "SELECT username, email, last_ip FROM account WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_LAST_ATTEMPT_IP, "SELECT last_attempt_ip FROM account WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_LAST_IP, "SELECT last_ip FROM account WHERE id = ?", CONNECTION_SYNCH); @@ -108,26 +114,30 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_INS_FALP_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES ((SELECT id FROM account WHERE username = ?), 0, 1, ?, ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS_BY_ID, "SELECT gmlevel, RealmID FROM account_access WHERE id = ? and (RealmID = ? OR RealmID = -1) ORDER BY gmlevel desc", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS, "SELECT permissionId, granted FROM rbac_account_permissions WHERE accountId = ? AND (realmId = ? OR realmId = -1) ORDER BY permissionId, realmId", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS, "SELECT permissionId, granted FROM rbac_account_permissions WHERE accountId = ? AND (realmId = ? OR realmId = -1) ORDER BY permissionId, realmId", CONNECTION_BOTH); PrepareStatement(LOGIN_INS_RBAC_ACCOUNT_PERMISSION, "INSERT INTO rbac_account_permissions (accountId, permissionId, granted, realmId) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE granted = VALUES(granted)", CONNECTION_ASYNC); PrepareStatement(LOGIN_DEL_RBAC_ACCOUNT_PERMISSION, "DELETE FROM rbac_account_permissions WHERE accountId = ? AND permissionId = ? AND (realmId = ? OR realmId = -1)", CONNECTION_ASYNC); PrepareStatement(LOGIN_INS_ACCOUNT_MUTE, "INSERT INTO account_muted VALUES (?, UNIX_TIMESTAMP(), ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_ACCOUNT_MUTE_INFO, "SELECT mutedate, mutetime, mutereason, mutedby FROM account_muted WHERE guid = ? ORDER BY mutedate ASC", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_INFO, "SELECT sha_pass_hash, id, locked, lock_country, last_ip, v, s FROM battlenet_accounts WHERE email = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_DEL_BNET_EXPIRED_BANS, "UPDATE battlenet_account_bans SET active = 0 WHERE active = 1 AND unbandate <> bandate AND unbandate <= UNIX_TIMESTAMP()", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_BNET_ACTIVE_ACCOUNT_BAN, "SELECT bandate, unbandate FROM battlenet_account_bans WHERE id = ? AND active = 1", CONNECTION_SYNCH); +#define BnetAccountInfo "ba.id, UPPER(ba.email), ba.locked, ba.lock_country, ba.last_ip, ba.failed_logins, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, bab.unbandate = bab.bandate" +#define BnetGameAccountInfo "a.id, a.username, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, ab.unbandate = ab.bandate, aa.gmlevel" + + PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_INFO, "SELECT " BnetAccountInfo ", ba.sha_pass_hash, ba.v, ba.s, " BnetGameAccountInfo + " FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account a ON ba.id = a.battlenet_account" + " LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_BNET_VS_FIELDS, "UPDATE battlenet_accounts SET v = ?, s = ? WHERE email = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_BNET_SESSION_KEY, "UPDATE battlenet_accounts SET sessionKey = ?, online = ? WHERE id = ?", CONNECTION_ASYNC); - PrepareStatement(LOGIN_SEL_BNET_RECONNECT_INFO, "SELECT ba.id, ba.sessionKey, a.id FROM battlenet_accounts ba LEFT JOIN account a ON ba.id = a.battlenet_account WHERE ba.email = ? AND a.username = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_BNET_GAME_ACCOUNTS, "SELECT a.id, a.username, ab.bandate, ab.unbandate, ab.active FROM account a LEFT JOIN account_banned ab ON a.id = ab.id WHERE battlenet_account = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_BNET_GAME_ACCOUNT, "SELECT a.id, a.username, ab.bandate, ab.unbandate, ab.active FROM account a LEFT JOIN account_banned ab ON a.id = ab.id WHERE username = ? AND battlenet_account = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_BNET_GAME_ACCOUNT_UNNAMED, "SELECT a.id, a.username, ab.bandate, ab.unbandate, ab.active FROM account a LEFT JOIN account_banned ab ON a.id = ab.id WHERE battlenet_index = ? AND battlenet_account = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_BNET_FAILED_LOGINS, "SELECT failed_logins FROM battlenet_accounts WHERE id = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_BNET_RECONNECT_INFO, "SELECT " BnetAccountInfo ", ba.sessionKey, " BnetGameAccountInfo + " FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account a ON ba.id = a.battlenet_account" + " LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ? AND a.username = ?", CONNECTION_ASYNC); + +#undef BnetGameAccountInfo +#undef BnetAccountInfo PrepareStatement(LOGIN_UPD_BNET_FAILED_LOGINS, "UPDATE battlenet_accounts SET failed_logins = failed_logins + 1 WHERE email = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_BNET_LAST_LOGIN_INFO, "UPDATE battlenet_accounts SET last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE id = ?", CONNECTION_ASYNC); - PrepareStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS, "SELECT rc.numchars, r.id, r.Region, r.Battlegroup, r.gamebuild FROM realmcharacters rc INNER JOIN realmlist r ON rc.realmid = r.id WHERE rc.acctid = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS, "SELECT rc.numchars, r.id, r.Region, r.Battlegroup, r.gamebuild FROM realmcharacters rc INNER JOIN realmlist r ON rc.realmid = r.id WHERE rc.acctid = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_INS_BNET_ACCOUNT, "INSERT INTO battlenet_accounts (`email`,`sha_pass_hash`) VALUES (?, ?)", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_EMAIL_BY_ID, "SELECT email FROM battlenet_accounts WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_ID_BY_EMAIL, "SELECT id FROM battlenet_accounts WHERE email = ?", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index db44ceb3fd2..71422c49bb8 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -46,6 +46,7 @@ enum LoginDatabaseStatements LOGIN_DEL_EXPIRED_IP_BANS, LOGIN_UPD_EXPIRED_ACCOUNT_BANS, LOGIN_SEL_IP_BANNED, + LOGIN_SEL_IP_INFO, LOGIN_INS_IP_AUTO_BANNED, LOGIN_SEL_ACCOUNT_BANNED, LOGIN_SEL_ACCOUNT_BANNED_ALL, @@ -108,8 +109,6 @@ enum LoginDatabaseStatements LOGIN_SEL_ACCOUNT_INFO, LOGIN_SEL_ACCOUNT_ACCESS_GMLEVEL_TEST, LOGIN_SEL_ACCOUNT_ACCESS, - LOGIN_SEL_ACCOUNT_RECRUITER, - LOGIN_SEL_BANS, LOGIN_SEL_ACCOUNT_WHOIS, LOGIN_SEL_REALMLIST_SECURITY_LEVEL, LOGIN_DEL_ACCOUNT, @@ -132,15 +131,9 @@ enum LoginDatabaseStatements LOGIN_SEL_ACCOUNT_MUTE_INFO, LOGIN_SEL_BNET_ACCOUNT_INFO, - LOGIN_DEL_BNET_EXPIRED_BANS, - LOGIN_SEL_BNET_ACTIVE_ACCOUNT_BAN, LOGIN_UPD_BNET_VS_FIELDS, LOGIN_UPD_BNET_SESSION_KEY, LOGIN_SEL_BNET_RECONNECT_INFO, - LOGIN_SEL_BNET_GAME_ACCOUNTS, - LOGIN_SEL_BNET_GAME_ACCOUNT, - LOGIN_SEL_BNET_GAME_ACCOUNT_UNNAMED, - LOGIN_SEL_BNET_FAILED_LOGINS, LOGIN_UPD_BNET_FAILED_LOGINS, LOGIN_UPD_BNET_LAST_LOGIN_INFO, LOGIN_SEL_BNET_CHARACTER_COUNTS, diff --git a/src/server/game/Accounts/RBAC.cpp b/src/server/game/Accounts/RBAC.cpp index 4653c139f1e..a8720b66df4 100644 --- a/src/server/game/Accounts/RBAC.cpp +++ b/src/server/game/Accounts/RBAC.cpp @@ -17,7 +17,6 @@ #include "RBAC.h" #include "AccountMgr.h" -#include "DatabaseEnv.h" #include "Log.h" namespace rbac @@ -180,7 +179,24 @@ void RBACData::LoadFromDB() stmt->setUInt32(0, GetId()); stmt->setInt32(1, GetRealmId()); - PreparedQueryResult result = LoginDatabase.Query(stmt); + LoadFromDBCallback(LoginDatabase.Query(stmt)); +} + +PreparedQueryResultFuture RBACData::LoadFromDBAsync() +{ + ClearData(); + + TC_LOG_DEBUG("rbac", "RBACData::LoadFromDB [Id: %u Name: %s]: Loading permissions", GetId(), GetName().c_str()); + // Load account permissions (granted and denied) that affect current realm + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS); + stmt->setUInt32(0, GetId()); + stmt->setInt32(1, GetRealmId()); + + return LoginDatabase.AsyncQuery(stmt); +} + +void RBACData::LoadFromDBCallback(PreparedQueryResult result) +{ if (result) { do @@ -190,8 +206,7 @@ void RBACData::LoadFromDB() GrantPermission(fields[0].GetUInt32()); else DenyPermission(fields[0].GetUInt32()); - } - while (result->NextRow()); + } while (result->NextRow()); } // Add default permissions diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index 71614a72731..24854b3e11a 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -40,7 +40,7 @@ #ifndef _RBAC_H #define _RBAC_H -#include "Define.h" +#include "DatabaseEnv.h" #include #include #include @@ -873,6 +873,8 @@ class RBACData /// Loads all permissions assigned to current account void LoadFromDB(); + PreparedQueryResultFuture LoadFromDBAsync(); + void LoadFromDBCallback(PreparedQueryResult result); /// Sets security level void SetSecurityLevel(uint8 id) diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 66d03352dcc..747dbc549c4 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -99,7 +99,7 @@ bool WorldSessionFilter::Process(WorldPacket* packet) } /// WorldSession constructor -WorldSession::WorldSession(uint32 id, uint32 battlenetAccountId, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter): +WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccountId, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter) : m_muteTime(mute_time), m_timeOutTime(0), AntiDOS(this), @@ -108,6 +108,7 @@ WorldSession::WorldSession(uint32 id, uint32 battlenetAccountId, std::shared_ptr m_Socket(sock), _security(sec), _accountId(id), + _accountName(std::move(name)), _battlenetAccountId(battlenetAccountId), m_expansion(expansion), _warden(NULL), @@ -192,11 +193,11 @@ std::string WorldSession::GetPlayerInfo() const { std::ostringstream ss; - ss << "[Player: " << GetPlayerName() << " ("; - if (_player != NULL) - ss << _player->GetGUID().ToString() << ", "; + ss << "[Player: "; + if (!m_playerLoading && _player) + ss << _player->GetName() << ' ' << _player->GetGUID().ToString() << ", "; - ss << "Account: " << GetAccountId() << ")]"; + ss << "Account: " << GetAccountId() << "]"; return ss.str(); } @@ -724,13 +725,6 @@ void WorldSession::SendAuthWaitQue(uint32 position) } } -void WorldSession::LoadGlobalAccountData() -{ - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_DATA); - stmt->setUInt32(0, GetAccountId()); - LoadAccountData(CharacterDatabase.Query(stmt), GLOBAL_CACHE_MASK); -} - void WorldSession::LoadAccountData(PreparedQueryResult result, uint32 mask) { for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i) @@ -806,13 +800,11 @@ void WorldSession::SendAccountDataTimes(uint32 mask) SendPacket(&data); } -void WorldSession::LoadTutorialsData() +void WorldSession::LoadTutorialsData(PreparedQueryResult result) { memset(m_Tutorials, 0, sizeof(uint32) * MAX_ACCOUNT_TUTORIAL_VALUES); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_TUTORIALS); - stmt->setUInt32(0, GetAccountId()); - if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) + if (result) for (uint8 i = 0; i < MAX_ACCOUNT_TUTORIAL_VALUES; ++i) m_Tutorials[i] = (*result)[i].GetUInt32(); @@ -845,7 +837,7 @@ void WorldSession::SaveTutorialsData(SQLTransaction &trans) m_TutorialsChanged = false; } -void WorldSession::ReadAddonsInfo(WorldPacket &data) +void WorldSession::ReadAddonsInfo(ByteBuffer &data) { if (data.rpos() + 4 > data.size()) return; @@ -1055,6 +1047,10 @@ void WorldSession::ProcessQueryCallbacks() { PreparedQueryResult result; + if (_realmAccountLoginCallback.valid() && _realmAccountLoginCallback.wait_for(std::chrono::seconds(0)) == std::future_status::ready && + _accountLoginCallback.valid() && _accountLoginCallback.wait_for(std::chrono::seconds(0)) == std::future_status::ready) + InitializeSessionCallback(_realmAccountLoginCallback.get(), _accountLoginCallback.get()); + //! HandleCharEnumOpcode if (_charEnumCallback.valid() && _charEnumCallback.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { @@ -1154,15 +1150,115 @@ void WorldSession::InitWarden(BigNumber* k, std::string const& os) void WorldSession::LoadPermissions() { uint32 id = GetAccountId(); - std::string name; - AccountMgr::GetName(id, name); uint8 secLevel = GetSecurity(); - _RBACData = new rbac::RBACData(id, name, realmHandle.Index, secLevel); - _RBACData->LoadFromDB(); - TC_LOG_DEBUG("rbac", "WorldSession::LoadPermissions [AccountId: %u, Name: %s, realmId: %d, secLevel: %u]", - id, name.c_str(), realmHandle.Index, secLevel); + id, _accountName.c_str(), realmHandle.Index, secLevel); + + _RBACData = new rbac::RBACData(id, _accountName, realmHandle.Index, secLevel); + _RBACData->LoadFromDB(); +} + +PreparedQueryResultFuture WorldSession::LoadPermissionsAsync() +{ + uint32 id = GetAccountId(); + uint8 secLevel = GetSecurity(); + TC_LOG_DEBUG("rbac", "WorldSession::LoadPermissions [AccountId: %u, Name: %s, realmId: %d, secLevel: %u]", + id, _accountName.c_str(), realmHandle.Index, secLevel); + + _RBACData = new rbac::RBACData(id, _accountName, realmHandle.Index, secLevel); + return _RBACData->LoadFromDBAsync(); +} + +class AccountInfoQueryHolderPerRealm : public SQLQueryHolder +{ +public: + enum + { + GLOBAL_ACCOUNT_DATA = 0, + TUTORIALS, + + MAX_QUERIES + }; + + AccountInfoQueryHolderPerRealm() { SetSize(MAX_QUERIES); } + + bool Initialize(uint32 accountId, uint32 /*battlenetAccountId*/) + { + bool ok = true; + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_DATA); + stmt->setUInt32(0, accountId); + ok = SetPreparedQuery(GLOBAL_ACCOUNT_DATA, stmt) && ok; + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_TUTORIALS); + stmt->setUInt32(0, accountId); + ok = SetPreparedQuery(TUTORIALS, stmt) && ok; + + return ok; + } +}; + +class AccountInfoQueryHolder : public SQLQueryHolder +{ +public: + enum + { + MAX_QUERIES + }; + + AccountInfoQueryHolder() { SetSize(MAX_QUERIES); } + + bool Initialize(uint32 /*accountId*/, uint32 /*battlenetAccountId*/) + { + bool ok = true; + + return ok; + } +}; + +void WorldSession::InitializeSession() +{ + AccountInfoQueryHolderPerRealm* realmHolder = new AccountInfoQueryHolderPerRealm(); + if (!realmHolder->Initialize(GetAccountId(), GetBattlenetAccountId())) + { + delete realmHolder; + SendAuthResponse(AUTH_SYSTEM_ERROR, false); + return; + } + + AccountInfoQueryHolder* holder = new AccountInfoQueryHolder(); + if (!holder->Initialize(GetAccountId(), GetBattlenetAccountId())) + { + delete realmHolder; + delete holder; + SendAuthResponse(AUTH_SYSTEM_ERROR, false); + return; + } + + _realmAccountLoginCallback = CharacterDatabase.DelayQueryHolder(realmHolder); + _accountLoginCallback = LoginDatabase.DelayQueryHolder(holder); +} + +void WorldSession::InitializeSessionCallback(SQLQueryHolder* realmHolder, SQLQueryHolder* holder) +{ + LoadAccountData(realmHolder->GetPreparedResult(AccountInfoQueryHolderPerRealm::GLOBAL_ACCOUNT_DATA), GLOBAL_CACHE_MASK); + LoadTutorialsData(realmHolder->GetPreparedResult(AccountInfoQueryHolderPerRealm::TUTORIALS)); + + if (!m_inQueue) + SendAuthResponse(AUTH_OK, false); + else + SendAuthWaitQue(0); + + SetInQueue(false); + ResetTimeOutTime(); + + SendAddonsInfo(); + SendClientCacheVersion(sWorld->getIntConfig(CONFIG_CLIENTCACHE_VERSION)); + SendTutorialsData(); + + delete realmHolder; + delete holder; } rbac::RBACData* WorldSession::GetRBACData() diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 61b647d9e30..422b4c94b99 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -255,7 +255,7 @@ struct PacketCounter class WorldSession { public: - WorldSession(uint32 id, uint32 battlenetAccountId, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter); + WorldSession(uint32 id, std::string&& name, uint32 battlenetAccountId, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter); ~WorldSession(); bool PlayerLoading() const { return m_playerLoading; } @@ -264,7 +264,7 @@ class WorldSession bool PlayerRecentlyLoggedOut() const { return m_playerRecentlyLogout; } bool PlayerDisconnected() const { return !m_Socket; } - void ReadAddonsInfo(WorldPacket& data); + void ReadAddonsInfo(ByteBuffer& data); void SendAddonsInfo(); bool IsAddonRegistered(const std::string& prefix) const; @@ -280,9 +280,13 @@ class WorldSession void SendAuthResponse(uint8 code, bool queued, uint32 queuePos = 0); void SendClientCacheVersion(uint32 version); + void InitializeSession(); + void InitializeSessionCallback(SQLQueryHolder* realmHolder, SQLQueryHolder* holder); + rbac::RBACData* GetRBACData(); bool HasPermission(uint32 permissionId); void LoadPermissions(); + PreparedQueryResultFuture LoadPermissionsAsync(); void InvalidateRBACData(); // Used to force LoadPermissions at next HasPermission check AccountTypes GetSecurity() const { return _security; } @@ -363,10 +367,9 @@ class WorldSession AccountData* GetAccountData(AccountDataType type) { return &m_accountData[type]; } void SetAccountData(AccountDataType type, time_t tm, std::string const& data); void SendAccountDataTimes(uint32 mask); - void LoadGlobalAccountData(); void LoadAccountData(PreparedQueryResult result, uint32 mask); - void LoadTutorialsData(); + void LoadTutorialsData(PreparedQueryResult result); void SendTutorialsData(); void SaveTutorialsData(SQLTransaction& trans); uint32 GetTutorialInt(uint8 index) const { return m_Tutorials[index]; } @@ -1035,6 +1038,8 @@ class WorldSession void InitializeQueryCallbackParameters(); void ProcessQueryCallbacks(); + QueryResultHolderFuture _realmAccountLoginCallback; + QueryResultHolderFuture _accountLoginCallback; PreparedQueryResultFuture _charEnumCallback; PreparedQueryResultFuture _addIgnoreCallback; PreparedQueryResultFuture _stablePetCallback; @@ -1104,6 +1109,7 @@ class WorldSession AccountTypes _security; uint32 _accountId; + std::string _accountName; uint32 _battlenetAccountId; uint8 m_expansion; diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 35f0610330b..bbaeed4528f 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -40,6 +40,40 @@ WorldSocket::WorldSocket(tcp::socket&& socket) void WorldSocket::Start() { + std::string ip_address = GetRemoteIpAddress().to_string(); + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO); + stmt->setString(0, ip_address); + stmt->setUInt32(1, inet_addr(ip_address.c_str())); + + _queryCallback = std::bind(&WorldSocket::CheckIpCallback, this, std::placeholders::_1); + _queryFuture = LoginDatabase.AsyncQuery(stmt); +} + +void WorldSocket::CheckIpCallback(PreparedQueryResult result) +{ + if (result) + { + bool banned = false; + do + { + Field* fields = result->Fetch(); + if (fields[0].GetUInt64() != 0) + banned = true; + + if (!fields[1].GetString().empty()) + _ipCountry = fields[1].GetString(); + + } while (result->NextRow()); + + if (banned) + { + SendAuthResponseError(AUTH_REJECT); + TC_LOG_ERROR("network", "WorldSocket::CheckIpCallback: Sent Auth Response (IP %s banned).", GetRemoteIpAddress().to_string().c_str()); + DelayedCloseSocket(); + return; + } + } + AsyncRead(); MessageBuffer initializer; @@ -51,6 +85,24 @@ void WorldSocket::Start() QueuePacket(std::move(initializer), dummy); } +bool WorldSocket::Update() +{ + if (!BaseSocket::Update()) + return false; + + { + std::lock_guard guard(_queryLock); + if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) + { + auto callback = std::move(_queryCallback); + _queryCallback = nullptr; + callback(_queryFuture.get()); + } + } + + return true; +} + void WorldSocket::HandleSendAuthSession() { WorldPacket packet(SMSG_AUTH_CHALLENGE, 37); @@ -122,13 +174,15 @@ void WorldSocket::ReadHandler() } // just received fresh new payload - if (!ReadDataHandler()) + ReadDataHandlerResult result = ReadDataHandler(); + _headerBuffer.Reset(); + if (result != ReadDataHandlerResult::Ok) { - CloseSocket(); + if (result != ReadDataHandlerResult::WaitingForQuery) + CloseSocket(); + return; } - - _headerBuffer.Reset(); } AsyncRead(); @@ -160,7 +214,79 @@ bool WorldSocket::ReadHeaderHandler() return true; } -bool WorldSocket::ReadDataHandler() +struct AuthSession +{ + uint8 LoginServerType = 0; + uint32 RealmID = 0; + uint16 Build = 0; + uint32 LocalChallenge = 0; + uint8 Digest[SHA_DIGEST_LENGTH] = {}; + std::string Account; + ByteBuffer AddonInfo; +}; + +struct AccountInfo +{ + struct + { + uint32 Id; + bool IsLockedToIP; + std::string LastIP; + std::string LockCountry; + LocaleConstant Locale; + std::string OS; + bool IsBanned; + } BattleNet; + + struct + { + uint32 Id; + BigNumber SessionKey; + uint8 Expansion; + int64 MuteTime; + uint32 Recruiter; + bool IsRecruiter; + AccountTypes Security; + bool IsBanned; + } Game; + + bool IsBanned() const { return BattleNet.IsBanned || Game.IsBanned; } + + explicit AccountInfo(Field* fields) + { + // 0 1 2 3 4 5 6 7 8 9 10 11 + // SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, ba.locale, a.recruiter, ba.os, ba.id, aa.gmLevel, + // 12 13 14 + // bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id + // FROM account a LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID IN (-1, ?) + // LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account_banned ab ON a.id = ab.id LEFT JOIN account r ON a.id = r.recruiter + // WHERE a.username = ? ORDER BY aa.RealmID DESC LIMIT 1 + Game.Id = fields[0].GetUInt32(); + Game.SessionKey.SetHexStr(fields[1].GetCString()); + BattleNet.LastIP = fields[2].GetString(); + BattleNet.IsLockedToIP = fields[3].GetBool(); + BattleNet.LockCountry = fields[4].GetString(); + Game.Expansion = fields[5].GetUInt8(); + Game.MuteTime = fields[6].GetInt64(); + BattleNet.Locale = LocaleConstant(fields[7].GetUInt8()); + Game.Recruiter = fields[8].GetUInt32(); + BattleNet.OS = fields[9].GetString(); + BattleNet.Id = fields[10].GetUInt32(); + Game.Security = AccountTypes(fields[11].GetUInt8()); + BattleNet.IsBanned = fields[12].GetUInt64() != 0; + Game.IsBanned = fields[13].GetUInt64() != 0; + Game.IsRecruiter = fields[14].GetUInt32() != 0; + + uint32 world_expansion = sWorld->getIntConfig(CONFIG_EXPANSION); + if (Game.Expansion > world_expansion) + Game.Expansion = world_expansion; + + if (BattleNet.Locale >= TOTAL_LOCALES) + BattleNet.Locale = LOCALE_enUS; + } +}; + +WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler() { if (_initialized) { @@ -179,7 +305,7 @@ bool WorldSocket::ReadDataHandler() { case CMSG_PING: LogOpcodeText(opcode, sessionGuard); - return HandlePing(packet); + return HandlePing(packet) ? ReadDataHandlerResult::Ok : ReadDataHandlerResult::Error; case CMSG_AUTH_SESSION: LogOpcodeText(opcode, sessionGuard); if (_worldSession) @@ -187,11 +313,11 @@ bool WorldSocket::ReadDataHandler() // locking just to safely log offending user is probably overkill but we are disconnecting him anyway if (sessionGuard.try_lock()) TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s", _worldSession->GetPlayerInfo().c_str()); - return false; + return ReadDataHandlerResult::Error; } HandleAuthSession(packet); - break; + return ReadDataHandlerResult::WaitingForQuery; case CMSG_KEEP_ALIVE: LogOpcodeText(opcode, sessionGuard); sScriptMgr->OnPacketReceive(_worldSession, packet); @@ -200,7 +326,7 @@ bool WorldSocket::ReadDataHandler() packet.rfinish(); // contains uint32 disconnectReason; TC_LOG_DEBUG("network", "%s", GetOpcodeNameForLogging(opcode).c_str()); sScriptMgr->OnPacketReceive(_worldSession, packet); - return true; + break; case CMSG_ENABLE_NAGLE: { TC_LOG_DEBUG("network", "%s", GetOpcodeNameForLogging(opcode).c_str()); @@ -217,21 +343,21 @@ bool WorldSocket::ReadDataHandler() { TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); CloseSocket(); - return false; + return ReadDataHandlerResult::Error; } // prevent invalid memory access/crash with custom opcodes if (opcode >= NUM_OPCODE_HANDLERS) { CloseSocket(); - return false; + return ReadDataHandlerResult::Error; } OpcodeHandler const* handler = opcodeTable[opcode]; if (!handler) { TC_LOG_ERROR("network.opcode", "No defined handler for opcode %s sent by %s", GetOpcodeNameForLogging(packet.GetOpcode()).c_str(), _worldSession->GetPlayerInfo().c_str()); - return true; + break; } // Our Idle timer will reset on any non PING opcodes. @@ -250,7 +376,7 @@ bool WorldSocket::ReadDataHandler() if (initializer != ClientConnectionInitialize) { CloseSocket(); - return false; + return ReadDataHandlerResult::Error; } _initialized = true; @@ -259,7 +385,7 @@ bool WorldSocket::ReadDataHandler() HandleSendAuthSession(); } - return true; + return ReadDataHandlerResult::Ok; } void WorldSocket::LogOpcodeText(uint16 opcode, std::unique_lock const& guard) const @@ -319,107 +445,88 @@ void WorldSocket::SendPacket(WorldPacket& packet) void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) { - uint8 digest[SHA_DIGEST_LENGTH]; - uint32 clientSeed; - uint8 security; - uint16 clientBuild; - uint32 id; uint32 addonSize; - LocaleConstant locale; - std::string account; - SHA1Hash sha; - BigNumber k; - bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED); - WorldPacket addonsData; - uint8 loginServerType; - uint32 realmIndex; + std::shared_ptr authSession = std::make_shared(); recvPacket.read_skip(); // ServerId - Used for GRUNT only recvPacket.read_skip(); // Battlegroup - recvPacket >> loginServerType; - recvPacket >> digest[10]; - recvPacket >> digest[18]; - recvPacket >> digest[12]; - recvPacket >> digest[5]; + recvPacket >> authSession->LoginServerType; + recvPacket >> authSession->Digest[10]; + recvPacket >> authSession->Digest[18]; + recvPacket >> authSession->Digest[12]; + recvPacket >> authSession->Digest[5]; recvPacket.read_skip(); - recvPacket >> digest[15]; - recvPacket >> digest[9]; - recvPacket >> digest[19]; - recvPacket >> digest[4]; - recvPacket >> digest[7]; - recvPacket >> digest[16]; - recvPacket >> digest[3]; - recvPacket >> clientBuild; - recvPacket >> digest[8]; - recvPacket >> realmIndex; + recvPacket >> authSession->Digest[15]; + recvPacket >> authSession->Digest[9]; + recvPacket >> authSession->Digest[19]; + recvPacket >> authSession->Digest[4]; + recvPacket >> authSession->Digest[7]; + recvPacket >> authSession->Digest[16]; + recvPacket >> authSession->Digest[3]; + recvPacket >> authSession->Build; + recvPacket >> authSession->Digest[8]; + recvPacket >> authSession->RealmID; recvPacket.read_skip(); - recvPacket >> digest[17]; - recvPacket >> digest[6]; - recvPacket >> digest[0]; - recvPacket >> digest[1]; - recvPacket >> digest[11]; - recvPacket >> clientSeed; - recvPacket >> digest[2]; + recvPacket >> authSession->Digest[17]; + recvPacket >> authSession->Digest[6]; + recvPacket >> authSession->Digest[0]; + recvPacket >> authSession->Digest[1]; + recvPacket >> authSession->Digest[11]; + recvPacket >> authSession->LocalChallenge; + recvPacket >> authSession->Digest[2]; recvPacket.read_skip(); // Region - recvPacket >> digest[14]; - recvPacket >> digest[13]; + recvPacket >> authSession->Digest[14]; + recvPacket >> authSession->Digest[13]; recvPacket >> addonSize; if (addonSize) { - addonsData.resize(addonSize); - recvPacket.read((uint8*)addonsData.contents(), addonSize); + authSession->AddonInfo.resize(addonSize); + recvPacket.read((uint8*)authSession->AddonInfo.contents(), addonSize); } recvPacket.ReadBit(); // UseIPv6 uint32 accountNameLength = recvPacket.ReadBits(12); - account = recvPacket.ReadString(accountNameLength); + authSession->Account = recvPacket.ReadString(accountNameLength); - // Get the account information from the auth database - // 0 1 2 3 4 5 6 7 8 9 - // SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os, battlenet_account FROM account WHERE username = ? PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME); - stmt->setString(0, account); + stmt->setInt32(0, realmHandle.Index); + stmt->setString(1, authSession->Account); - PreparedQueryResult result = LoginDatabase.Query(stmt); + { + std::lock_guard guard(_queryLock); + _queryCallback = io_service().wrap(std::bind(&WorldSocket::HandleAuthSessionCallback, this, authSession, std::placeholders::_1)); + _queryFuture = LoginDatabase.AsyncQuery(stmt); + } +} +void WorldSocket::HandleAuthSessionCallback(std::shared_ptr authSession, PreparedQueryResult result) +{ // Stop if the account is not found if (!result) { // We can not log here, as we do not know the account. Thus, no accountId. SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account %s).", account.c_str()); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account %s).", authSession->Account.c_str()); DelayedCloseSocket(); return; } - Field* fields = result->Fetch(); - - uint8 expansion = fields[4].GetUInt8(); - uint32 world_expansion = sWorld->getIntConfig(CONFIG_EXPANSION); - if (expansion > world_expansion) - expansion = world_expansion; + AccountInfo account(result->Fetch()); // For hook purposes, we get Remoteaddress at this point. std::string address = GetRemoteIpAddress().to_string(); // As we don't know if attempted login process by ip works, we update last_attempt_ip right away - stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP); - + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP); stmt->setString(0, address); - stmt->setString(1, account); - + stmt->setString(1, authSession->Account); LoginDatabase.Execute(stmt); // This also allows to check for possible "hack" attempts on account - // id has to be fetched at this point, so that first actual account response that fails can be logged - id = fields[0].GetUInt32(); - - k.SetHexStr(fields[1].GetCString()); - // even if auth credentials are bad, try using the session key we have - client cannot read auth response error without it - _authCrypt.Init(&k); + _authCrypt.Init(&account.Game.SessionKey); // First reject the connection if packet contains invalid data or realm state doesn't allow logging in if (sWorld->IsClosed()) @@ -430,7 +537,7 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) return; } - if (realmIndex != realmHandle.Index) + if (authSession->RealmID != realmHandle.Index) { SendAuthResponseError(REALM_LIST_REALM_NOT_FOUND); TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (bad realm)."); @@ -438,13 +545,12 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) return; } - std::string os = fields[8].GetString(); - // Must be done before WorldSession is created - if (wardenActive && os != "Win" && os != "OSX") + bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED); + if (wardenActive && account.BattleNet.OS != "Win" && account.BattleNet.OS != "OSX") { SendAuthResponseError(AUTH_REJECT); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", address.c_str(), os.c_str()); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", address.c_str(), account.BattleNet.OS.c_str()); DelayedCloseSocket(); return; } @@ -452,139 +558,109 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) // Check that Key and account name are the same on client and server uint32 t = 0; - sha.UpdateData(account); + SHA1Hash sha; + sha.UpdateData(authSession->Account); sha.UpdateData((uint8*)&t, 4); - sha.UpdateData((uint8*)&clientSeed, 4); + sha.UpdateData((uint8*)&authSession->LocalChallenge, 4); sha.UpdateData((uint8*)&_authSeed, 4); - sha.UpdateBigNumbers(&k, NULL); + sha.UpdateBigNumbers(&account.Game.SessionKey, NULL); sha.Finalize(); - if (memcmp(sha.GetDigest(), digest, SHA_DIGEST_LENGTH) != 0) + if (memcmp(sha.GetDigest(), authSession->Digest, SHA_DIGEST_LENGTH) != 0) { SendAuthResponseError(AUTH_FAILED); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", id, account.c_str(), address.c_str()); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", account.Game.Id, authSession->Account.c_str(), address.c_str()); DelayedCloseSocket(); return; } ///- Re-check ip locking (same check as in auth). - if (fields[3].GetUInt8() == 1) // if ip is locked + if (account.BattleNet.IsLockedToIP) // if ip is locked { - if (strcmp(fields[2].GetCString(), address.c_str()) != 0) + if (account.BattleNet.LastIP != address) { SendAuthResponseError(AUTH_FAILED); - TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s).", fields[2].GetCString(), address.c_str()); + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s).", account.BattleNet.LastIP.c_str(), address.c_str()); // We could log on hook only instead of an additional db log, however action logger is config based. Better keep DB logging as well - sScriptMgr->OnFailedAccountLogin(id); + sScriptMgr->OnFailedAccountLogin(account.Game.Id); + DelayedCloseSocket(); + return; + } + } + else if (!account.BattleNet.LockCountry.empty() && account.BattleNet.LockCountry != "00" && !_ipCountry.empty()) + { + if (account.BattleNet.LockCountry != _ipCountry) + { + SendAuthResponseError(AUTH_FAILED); + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account country differs. Original country: %s, new country: %s).", account.BattleNet.LockCountry.c_str(), _ipCountry.c_str()); + // We could log on hook only instead of an additional db log, however action logger is config based. Better keep DB logging as well + sScriptMgr->OnFailedAccountLogin(account.Game.Id); DelayedCloseSocket(); return; } } - int64 mutetime = fields[5].GetInt64(); + int64 mutetime = account.Game.MuteTime; //! Negative mutetime indicates amount of seconds to be muted effective on next login - which is now. if (mutetime < 0) { mutetime = time(NULL) + llabs(mutetime); stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME_LOGIN); - stmt->setInt64(0, mutetime); - stmt->setUInt32(1, id); - + stmt->setUInt32(1, account.Game.Id); LoginDatabase.Execute(stmt); } - locale = LocaleConstant(fields[6].GetUInt8()); - if (locale >= TOTAL_LOCALES) - locale = LOCALE_enUS; - - uint32 recruiter = fields[7].GetUInt32(); - - uint32 battlenetAccountId = 0; - if (loginServerType == 1) - battlenetAccountId = fields[9].GetUInt32(); - - // Checks gmlevel per Realm - stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); - - stmt->setUInt32(0, id); - stmt->setInt32(1, int32(realmHandle.Index)); - - result = LoginDatabase.Query(stmt); - - if (!result) - security = 0; - else - { - fields = result->Fetch(); - security = fields[0].GetUInt8(); - } - - // Re-check account ban (same check as in auth) - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BANS); - - stmt->setUInt32(0, id); - stmt->setString(1, address); - - PreparedQueryResult banresult = LoginDatabase.Query(stmt); - - if (banresult) // if account banned + if (account.IsBanned()) { SendAuthResponseError(AUTH_BANNED); TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)."); - sScriptMgr->OnFailedAccountLogin(id); + sScriptMgr->OnFailedAccountLogin(account.Game.Id); DelayedCloseSocket(); return; } // Check locked state for server AccountTypes allowedAccountType = sWorld->GetPlayerSecurityLimit(); - TC_LOG_DEBUG("network", "Allowed Level: %u Player Level %u", allowedAccountType, AccountTypes(security)); - if (allowedAccountType > SEC_PLAYER && AccountTypes(security) < allowedAccountType) + TC_LOG_DEBUG("network", "Allowed Level: %u Player Level %u", allowedAccountType, account.Game.Security); + if (allowedAccountType > SEC_PLAYER && account.Game.Security < allowedAccountType) { SendAuthResponseError(AUTH_UNAVAILABLE); TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); - sScriptMgr->OnFailedAccountLogin(id); + sScriptMgr->OnFailedAccountLogin(account.Game.Id); DelayedCloseSocket(); return; } - TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.", - account.c_str(), - address.c_str()); - - // Check if this user is by any chance a recruiter - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_RECRUITER); - - stmt->setUInt32(0, id); - - result = LoginDatabase.Query(stmt); - - bool isRecruiter = false; - if (result) - isRecruiter = true; + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.", authSession->Account.c_str(), address.c_str()); // Update the last_ip in the database as it was successful for login stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_IP); - stmt->setString(0, address); - stmt->setString(1, account); - + stmt->setString(1, authSession->Account); LoginDatabase.Execute(stmt); // At this point, we can safely hook a successful login - sScriptMgr->OnAccountLogin(id); + sScriptMgr->OnAccountLogin(account.Game.Id); - _worldSession = new WorldSession(id, battlenetAccountId, shared_from_this(), AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter); - _worldSession->LoadGlobalAccountData(); - _worldSession->LoadTutorialsData(); - _worldSession->ReadAddonsInfo(addonsData); - _worldSession->LoadPermissions(); + _worldSession = new WorldSession(account.Game.Id, std::move(authSession->Account), account.BattleNet.Id, shared_from_this(), account.Game.Security, + account.Game.Expansion, mutetime, account.BattleNet.Locale, account.Game.Recruiter, account.Game.IsRecruiter); + _worldSession->ReadAddonsInfo(authSession->AddonInfo); // Initialize Warden system only if it is enabled by config if (wardenActive) - _worldSession->InitWarden(&k, os); + _worldSession->InitWarden(&account.Game.SessionKey, account.BattleNet.OS); + + _queryCallback = io_service().wrap(std::bind(&WorldSocket::LoadSessionPermissionsCallback, this, std::placeholders::_1)); + _queryFuture = _worldSession->LoadPermissionsAsync(); + AsyncRead(); +} + +void WorldSocket::LoadSessionPermissionsCallback(PreparedQueryResult result) +{ + // RBAC must be loaded before adding session to check for skip queue permission + _worldSession->GetRBACData()->LoadFromDBCallback(result); sWorld->AddSession(_worldSession); } diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index b1fa8ba658a..348e4aeaa98 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -45,12 +45,16 @@ struct ClientPktHeader #pragma pack(pop) +struct AuthSession; + class WorldSocket : public Socket { static std::string const ServerConnectionInitialize; static std::string const ClientConnectionInitialize; + typedef Socket BaseSocket; + public: WorldSocket(tcp::socket&& socket); @@ -58,6 +62,7 @@ public: WorldSocket& operator=(WorldSocket const& right) = delete; void Start() override; + bool Update() override; void SendPacket(WorldPacket& packet); @@ -65,9 +70,19 @@ protected: void OnClose() override; void ReadHandler() override; bool ReadHeaderHandler(); - bool ReadDataHandler(); + + enum class ReadDataHandlerResult + { + Ok = 0, + Error = 1, + WaitingForQuery = 2 + }; + + ReadDataHandlerResult ReadDataHandler(); private: + void CheckIpCallback(PreparedQueryResult result); + /// writes network.opcode log /// accessing WorldSession is not threadsafe, only do it when holding _worldSessionLock void LogOpcodeText(uint16 opcode, std::unique_lock const& guard) const; @@ -75,6 +90,8 @@ private: void SendPacketAndLogOpcode(WorldPacket& packet); void HandleSendAuthSession(); void HandleAuthSession(WorldPacket& recvPacket); + void HandleAuthSessionCallback(std::shared_ptr authSession, PreparedQueryResult result); + void LoadSessionPermissionsCallback(PreparedQueryResult result); void SendAuthResponseError(uint8 code); bool HandlePing(WorldPacket& recvPacket); @@ -93,6 +110,11 @@ private: MessageBuffer _packetBuffer; bool _initialized; + + std::mutex _queryLock; + PreparedQueryResultFuture _queryFuture; + std::function _queryCallback; + std::string _ipCountry; }; #endif diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 1b98221ec5f..c2568d1fd45 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -270,10 +270,7 @@ void World::AddSession_(WorldSession* s) return; } - s->SendAuthResponse(AUTH_OK, false); - s->SendAddonsInfo(); - s->SendClientCacheVersion(sWorld->getIntConfig(CONFIG_CLIENTCACHE_VERSION)); - s->SendTutorialsData(); + s->InitializeSession(); UpdateMaxSessionCounters(); @@ -363,15 +360,7 @@ bool World::RemoveQueuedPlayer(WorldSession* sess) if ((!m_playerLimit || sessions < m_playerLimit) && !m_QueuedPlayer.empty()) { WorldSession* pop_sess = m_QueuedPlayer.front(); - pop_sess->SetInQueue(false); - pop_sess->ResetTimeOutTime(); - pop_sess->SendAuthWaitQue(0); - pop_sess->SendAddonsInfo(); - - pop_sess->SendClientCacheVersion(sWorld->getIntConfig(CONFIG_CLIENTCACHE_VERSION)); - pop_sess->SendAccountDataTimes(GLOBAL_CACHE_MASK); - pop_sess->SendTutorialsData(); - + pop_sess->InitializeSession(); m_QueuedPlayer.pop_front(); // update iter to point first queued socket or end() if queue is empty now diff --git a/src/server/shared/Networking/MessageBuffer.h b/src/server/shared/Networking/MessageBuffer.h index 0473c10d4fd..189a56f18b6 100644 --- a/src/server/shared/Networking/MessageBuffer.h +++ b/src/server/shared/Networking/MessageBuffer.h @@ -84,9 +84,9 @@ public: // Ensures there's "some" free space, make sure to call Normalize() before this void EnsureFreeSpace() { - // Double the size of the buffer if it's already full + // resize buffer if it's already full if (GetRemainingSpace() == 0) - _storage.resize(_storage.size() * 2); + _storage.resize(_storage.size() * 3 / 2); } void Write(void const* data, std::size_t size) diff --git a/src/server/shared/Networking/Socket.h b/src/server/shared/Networking/Socket.h index b45f47421d8..a2f57b5029e 100644 --- a/src/server/shared/Networking/Socket.h +++ b/src/server/shared/Networking/Socket.h @@ -160,6 +160,8 @@ protected: MessageBuffer _writeBuffer; #endif + boost::asio::io_service& io_service() { return _socket.get_io_service(); } + private: void ReadHandlerInternal(boost::system::error_code error, size_t transferredBytes) {