diff options
-rw-r--r-- | src/server/authserver/Server/AuthSession.cpp | 12 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocket.cpp | 121 | ||||
-rw-r--r-- | src/server/shared/Networking/Socket.h | 10 |
3 files changed, 82 insertions, 61 deletions
diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp index 9931595e860..8276183fb10 100644 --- a/src/server/authserver/Server/AuthSession.cpp +++ b/src/server/authserver/Server/AuthSession.cpp @@ -83,8 +83,8 @@ typedef struct AUTH_LOGON_PROOF_S uint8 cmd; uint8 error; uint8 M2[20]; - uint32 unk1; - uint32 unk2; + uint32 AccountFlags; + uint32 SurveyId; uint16 unk3; } sAuthLogonProof_S; @@ -540,9 +540,9 @@ bool AuthSession::HandleLogonProof() memcpy(proof.M2, sha.GetDigest(), 20); proof.cmd = AUTH_LOGON_PROOF; proof.error = 0; - proof.unk1 = 0x00800000; // Accountflags. 0x01 = GM, 0x08 = Trial, 0x00800000 = Pro pass (arena tournament) - proof.unk2 = 0x00; // SurveyId - proof.unk3 = 0x00; + proof.AccountFlags = 0x00800000; // 0x01 = GM, 0x08 = Trial, 0x00800000 = Pro pass (arena tournament) + proof.SurveyId = 0; + proof.unk3 = 0; packet.resize(sizeof(proof)); std::memcpy(packet.contents(), &proof, sizeof(proof)); @@ -831,7 +831,7 @@ bool AuthSession::HandleRealmList() pkt << AmountOfCharacters; pkt << realm.timezone; // realm category if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients - pkt << uint8(0x2C); // unk, may be realm number/id? + pkt << uint8(realm.m_ID); else pkt << uint8(0x0); // 1.12.1 and 1.12.2 clients diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 5945d9fe868..ca8e2cd5a34 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -87,8 +87,6 @@ void WorldSocket::ReadDataHandler() { ClientPktHeader* header = reinterpret_cast<ClientPktHeader*>(GetHeaderBuffer()); - header->size -= sizeof(header->cmd); - uint16 opcode = uint16(header->cmd); std::string opcodeName = GetOpcodeNameForLogging(opcode); @@ -173,35 +171,30 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) std::string account; SHA1Hash sha; uint32 clientBuild; - uint32 unk2, unk3, unk5, unk6, unk7; + uint32 serverId, loginServerType, region, battlegroup, realmIndex; uint64 unk4; WorldPacket packet, SendAddonPacked; BigNumber k; bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED); - if (sWorld->IsClosed()) - { - SendAuthResponseError(AUTH_REJECT); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: World closed, denying client (%s).", GetRemoteIpAddress().to_string().c_str()); - return; - } - // Read the content of the packet recvPacket >> clientBuild; - recvPacket >> unk2; + recvPacket >> serverId; // Used for GRUNT only recvPacket >> account; - recvPacket >> unk3; + recvPacket >> loginServerType; // 0 GRUNT, 1 Battle.net recvPacket >> clientSeed; - recvPacket >> unk5 >> unk6 >> unk7; + recvPacket >> region >> battlegroup; // Used for Battle.net only + recvPacket >> realmIndex; // realmId from auth_database.realmlist table recvPacket >> unk4; recvPacket.read(digest, 20); - TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, unk3 %u, clientseed %u", + TC_LOG_INFO("network", "WorldSocket::HandleAuthSession: client %u, serverId %u, account %s, loginServerType %u, clientseed %u, realmIndex %u", clientBuild, - unk2, + serverId, account.c_str(), - unk3, - clientSeed); + loginServerType, + clientSeed, + realmIndex); // Get the account information from the auth database // 0 1 2 3 4 5 6 7 8 @@ -218,6 +211,7 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) // 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)."); + DelayedCloseSocket(); return; } @@ -243,6 +237,57 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) // 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); + + // First reject the connection if packet contains invalid data or realm state doesn't allow logging in + if (sWorld->IsClosed()) + { + SendAuthResponseError(AUTH_REJECT); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: World closed, denying client (%s).", GetRemoteIpAddress().to_string().c_str()); + DelayedCloseSocket(); + return; + } + + if (realmIndex != realmID) + { + SendAuthResponseError(REALM_LIST_REALM_NOT_FOUND); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (bad realm)."); + DelayedCloseSocket(); + return; + } + + std::string os = fields[8].GetString(); + + // Must be done before WorldSession is created + if (wardenActive && os != "Win" && 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()); + DelayedCloseSocket(); + return; + } + + // Check that Key and account name are the same on client and server + uint32 t = 0; + + sha.UpdateData(account); + sha.UpdateData((uint8*)&t, 4); + sha.UpdateData((uint8*)&clientSeed, 4); + sha.UpdateData((uint8*)&_authSeed, 4); + sha.UpdateBigNumbers(&k, NULL); + sha.Finalize(); + + if (memcmp(sha.GetDigest(), digest, 20)) + { + SendAuthResponseError(AUTH_FAILED); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", id, 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 { @@ -252,12 +297,11 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s).", fields[2].GetCString(), 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); + DelayedCloseSocket(); return; } } - k.SetHexStr(fields[1].GetCString()); - int64 mutetime = fields[5].GetInt64(); //! Negative mutetime indicates amount of seconds to be muted effective on next login - which is now. if (mutetime < 0) @@ -277,16 +321,6 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) locale = LOCALE_enUS; uint32 recruiter = fields[7].GetUInt32(); - std::string os = fields[8].GetString(); - - // Must be done before WorldSession is created - if (wardenActive && os != "Win" && 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()); - return; - } - // Checks gmlevel per Realm stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); @@ -316,6 +350,7 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) SendAuthResponseError(AUTH_BANNED); TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)."); sScriptMgr->OnFailedAccountLogin(id); + DelayedCloseSocket(); return; } @@ -327,23 +362,7 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) SendAuthResponseError(AUTH_UNAVAILABLE); TC_LOG_INFO("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); sScriptMgr->OnFailedAccountLogin(id); - return; - } - - // Check that Key and account name are the same on client and server - uint32 t = 0; - - sha.UpdateData(account); - sha.UpdateData((uint8*)&t, 4); - sha.UpdateData((uint8*)&clientSeed, 4); - sha.UpdateData((uint8*)&_authSeed, 4); - sha.UpdateBigNumbers(&k, NULL); - sha.Finalize(); - - if (memcmp(sha.GetDigest(), digest, 20)) - { - SendAuthResponseError(AUTH_FAILED); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", id, account.c_str(), address.c_str()); + DelayedCloseSocket(); return; } @@ -370,19 +389,15 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) LoginDatabase.Execute(stmt); - // NOTE ATM the socket is single-threaded, have this in mind ... - _worldSession = new WorldSession(id, shared_from_this(), AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter); - - _authCrypt.Init(&k); + // At this point, we can safely hook a successful login + sScriptMgr->OnAccountLogin(id); + _worldSession = new WorldSession(id, shared_from_this(), AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter); _worldSession->LoadGlobalAccountData(); _worldSession->LoadTutorialsData(); _worldSession->ReadAddonsInfo(recvPacket); _worldSession->LoadPermissions(); - // At this point, we can safely hook a successful login - sScriptMgr->OnAccountLogin(id); - // Initialize Warden system only if it is enabled by config if (wardenActive) _worldSession->InitWarden(&k, os); diff --git a/src/server/shared/Networking/Socket.h b/src/server/shared/Networking/Socket.h index a13a079ff6c..3bd30bd731b 100644 --- a/src/server/shared/Networking/Socket.h +++ b/src/server/shared/Networking/Socket.h @@ -42,7 +42,7 @@ class Socket : public std::enable_shared_from_this<T> public: Socket(tcp::socket&& socket, std::size_t headerSize) : _socket(std::move(socket)), _remoteAddress(_socket.remote_endpoint().address()), - _remotePort(_socket.remote_endpoint().port()), _readHeaderBuffer(), _readDataBuffer(), _closed(false) + _remotePort(_socket.remote_endpoint().port()), _readHeaderBuffer(), _readDataBuffer(), _closed(false), _closing(false) { _readHeaderBuffer.Grow(headerSize); } @@ -126,7 +126,7 @@ public: std::placeholders::_1, std::placeholders::_2)); } - bool IsOpen() const { return !_closed; } + bool IsOpen() const { return !_closed && !_closing; } virtual void CloseSocket() { @@ -140,6 +140,9 @@ public: shutdownError.value(), shutdownError.message().c_str()); } + /// Marks the socket for closing after write buffer becomes empty + void DelayedCloseSocket() { _closing = true; } + virtual bool IsHeaderReady() const { return _readHeaderBuffer.IsMessageReady(); } virtual bool IsDataReady() const { return _readDataBuffer.IsMessageReady(); } @@ -221,6 +224,8 @@ private: if (!_writeQueue.empty()) AsyncWrite(_writeQueue.front()); + else if (_closing) + CloseSocket(); } else CloseSocket(); @@ -241,6 +246,7 @@ private: MessageBuffer _readDataBuffer; std::atomic<bool> _closed; + std::atomic<bool> _closing; }; #endif // __SOCKET_H__ |