Core/NetworkIO: implement 2nd connection

This commit is contained in:
Ovahlord committed 2019-10-03 22:32:24 +02:00
1 parent 0299e10945
commit 8bec6af24a
28 files changed
+2049 -1000

No files matched your search

+146 -64
View File
@@ -16,6 +16,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AuthenticationPackets.h"
#include "WorldSocket.h"
#include "BattlenetAccountMgr.h"
#include "BigNumber.h"
@@ -30,6 +31,7 @@
#include "ScriptMgr.h"
#include "SHA1.h"
#include "World.h"
#include <zlib.h>
#include <memory>
class EncryptablePacket : public WorldPacket
@@ -46,16 +48,25 @@ private:
using boost::asio::ip::tcp;
std::string const WorldSocket::ServerConnectionInitialize("WORLD OF WARCRAFT CONNECTION - SERVER TO CLIENT");
std::string const WorldSocket::ClientConnectionInitialize("WORLD OF WARCRAFT CONNECTION - CLIENT TO SERVER");
WorldSocket::WorldSocket(tcp::socket&& socket)
: Socket(std::move(socket)), _authSeed(rand32()), _OverSpeedPings(0), _worldSession(nullptr), _sendBufferSize(4096), _initialized(false)
WorldSocket::WorldSocket(tcp::socket&& socket) : Socket(std::move(socket)),
_authSeed(rand32()), _OverSpeedPings(0), _worldSession(nullptr), _authed(false),
_sendBufferSize(4096), _initialized(false), _type(CONNECTION_TYPE_REALM),
_compressionStream(nullptr)
{
_headerBuffer.Resize(2);
}
WorldSocket::~WorldSocket()
{
if (_compressionStream)
{
deflateEnd(_compressionStream);
delete _compressionStream;
}
}
void WorldSocket::Start()
{
std::string ip_address = GetRemoteIpAddress().to_string();
@@ -104,8 +115,8 @@ bool WorldSocket::Update()
MessageBuffer buffer(_sendBufferSize);
while (_bufferQueue.Dequeue(queued))
{
if (_worldSession && queued->size() > 0x400 && !queued->IsCompressed())
queued->Compress(_worldSession->GetCompressionStream());
if (queued->size() > 0x400 && !queued->IsCompressed())
queued->Compress(_compressionStream);
ServerPktHeader header(queued->size() + 2, queued->GetOpcode());
if (queued->NeedsEncryption())
@@ -150,13 +161,11 @@ bool WorldSocket::Update()
void WorldSocket::HandleSendAuthSession()
{
WorldPacket packet(SMSG_AUTH_CHALLENGE, 37);
BigNumber seed1;
seed1.SetRand(16 * 8);
packet.append(seed1.AsByteArray(16).get(), 16); // new encryption seeds
_encryptSeed.SetRand(16 * 8);
_decryptSeed.SetRand(16 * 8);
BigNumber seed2;
seed2.SetRand(16 * 8);
packet.append(seed2.AsByteArray(16).get(), 16); // new encryption seeds
packet.append(_encryptSeed.AsByteArray(16).get(), 16); // new encryption seeds
packet.append(_decryptSeed.AsByteArray(16).get(), 16); // new encryption seeds
packet << uint32(_authSeed);
packet << uint8(1);
@@ -232,6 +241,13 @@ void WorldSocket::ReadHandler()
AsyncRead();
}
void WorldSocket::SetWorldSession(WorldSession* session)
{
std::lock_guard<std::mutex> sessionGuard(_worldSessionLock);
_worldSession = session;
_authed = true;
}
bool WorldSocket::ReadHeaderHandler()
{
ASSERT(_headerBuffer.GetActiveSize() == (_initialized ? sizeof(ClientPktHeader) : 2));
@@ -338,7 +354,7 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
OpcodeClient opcode = static_cast<OpcodeClient>(header->cmd);
WorldPacket packet(opcode, std::move(_packetBuffer));
WorldPacket packet(opcode, std::move(_packetBuffer), GetConnectionType());
WorldPacket* packetToQueue;
if (sPacketLog->CanLogPacket())
@@ -364,7 +380,7 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
case CMSG_AUTH_SESSION:
{
LogOpcodeText(opcode, sessionGuard);
if (_worldSession)
if (_authed)
{
// locking just to safely log offending user is probably overkill but we are disconnecting him anyway
if (sessionGuard.try_lock())
@@ -374,7 +390,9 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
try
{
HandleAuthSession(packet);
WorldPackets::Auth::AuthSession authSession(std::move(packet));
authSession.Read();
HandleAuthSession(authSession);
return ReadDataHandlerResult::WaitingForQuery;
}
catch (ByteBufferException const&)
@@ -383,13 +401,37 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_AUTH_SESSION", GetRemoteIpAddress().to_string().c_str());
return ReadDataHandlerResult::Error;
}
case CMSG_AUTH_CONTINUED_SESSION:
{
LogOpcodeText(opcode, sessionGuard);
if (_authed)
{
// 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_CONTINUED_SESSION from %s", _worldSession->GetPlayerInfo().c_str());
return ReadDataHandlerResult::Error;
}
try
{
std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession = std::make_shared<WorldPackets::Auth::AuthContinuedSession>(std::move(packet));
authSession->Read();
HandleAuthContinuedSession(authSession);
return ReadDataHandlerResult::WaitingForQuery;
}
catch (ByteBufferException const&)
{
}
TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_AUTH_CONTINUED_SESSION", GetRemoteIpAddress().to_string().c_str());
return ReadDataHandlerResult::Error;
}
case CMSG_LOG_DISCONNECT:
packet.rfinish(); // contains uint32 disconnectReason;
TC_LOG_DEBUG("network", "%s", GetOpcodeNameForLogging(opcode).c_str());
LogOpcodeText(opcode, sessionGuard);
sScriptMgr->OnPacketReceive(_worldSession, packet);
return ReadDataHandlerResult::Ok;
case CMSG_ENABLE_NAGLE:
TC_LOG_DEBUG("network", "%s", GetOpcodeNameForLogging(opcode).c_str());
LogOpcodeText(opcode, sessionGuard);
sScriptMgr->OnPacketReceive(_worldSession, packet);
if (_worldSession)
_worldSession->HandleEnableNagleAlgorithm();
@@ -446,6 +488,20 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
return ReadDataHandlerResult::Error;
}
_compressionStream = new z_stream();
_compressionStream->zalloc = (alloc_func)nullptr;
_compressionStream->zfree = (free_func)nullptr;
_compressionStream->opaque = (voidpf)nullptr;
_compressionStream->avail_in = 0;
_compressionStream->next_in = nullptr;
int32 z_res = deflateInit(_compressionStream, sWorld->getIntConfig(CONFIG_COMPRESSION));
if (z_res != Z_OK)
{
TC_LOG_ERROR("network", "Can't initialize packet compression (zlib: deflateInit) Error code: %i (%s)", z_res, zError(z_res));
CloseSocket();
return ReadDataHandlerResult::Error;
}
_initialized = true;
_headerBuffer.Resize(sizeof(ClientPktHeader));
_packetBuffer.Reset();
@@ -485,58 +541,22 @@ void WorldSocket::SendPacket(WorldPacket const& packet)
_bufferQueue.Enqueue(new EncryptablePacket(packet, _authCrypt.IsInitialized()));
}
void WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
void WorldSocket::HandleAuthSession(WorldPackets::Auth::AuthSession& authSession)
{
uint32 addonSize;
std::shared_ptr<AuthSession> authSession = std::make_shared<AuthSession>();
recvPacket.read_skip<uint32>(); // ServerId - Used for GRUNT only
recvPacket.read_skip<uint32>(); // Battlegroup
recvPacket >> authSession->LoginServerType;
recvPacket >> authSession->Digest[10];
recvPacket >> authSession->Digest[18];
recvPacket >> authSession->Digest[12];
recvPacket >> authSession->Digest[5];
recvPacket.read_skip<uint64>();
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<uint8>();
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<uint32>(); // Region
recvPacket >> authSession->Digest[14];
recvPacket >> authSession->Digest[13];
recvPacket >> addonSize;
if (addonSize)
{
authSession->AddonInfo.resize(addonSize);
recvPacket.read((uint8*)authSession->AddonInfo.contents(), addonSize);
}
recvPacket.ReadBit(); // UseIPv6
uint32 accountNameLength = recvPacket.ReadBits(12);
authSession->Account = recvPacket.ReadString(accountNameLength);
std::shared_ptr<AuthSession> session = std::make_shared<AuthSession>();
session->AddonInfo = authSession.AddonInfo;
session->Account = authSession.Account;
session->Build = authSession.Build;
memcpy(session->Digest, authSession.Digest, SHA_DIGEST_LENGTH);
session->LocalChallenge = authSession.LocalChallenge;
session->LoginServerType = authSession.LoginServerType;
session->RealmID = authSession.RealmID;
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME);
stmt->setInt32(0, int32(realm.Id.Realm));
stmt->setString(1, authSession->Account);
stmt->setString(1, session->Account);
_queryProcessor.AddQuery(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSocket::HandleAuthSessionCallback, this, authSession, std::placeholders::_1)));
_queryProcessor.AddQuery(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSocket::HandleAuthSessionCallback, this, session, std::placeholders::_1)));
}
void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSession, PreparedQueryResult result)
@@ -686,6 +706,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSes
// At this point, we can safely hook a successful login
sScriptMgr->OnAccountLogin(account.Game.Id);
_authed = true;
_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);
@@ -706,6 +727,67 @@ void WorldSocket::LoadSessionPermissionsCallback(PreparedQueryResult result)
sWorld->AddSession(_worldSession);
}
void WorldSocket::HandleAuthContinuedSession(std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession)
{
WorldSession::ConnectToKey key;
key.Raw = authSession->Key;
_type = ConnectionType(key.Fields.ConnectionType);
if (_type != CONNECTION_TYPE_INSTANCE)
{
SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT);
DelayedCloseSocket();
return;
}
// Client switches packet headers after sending CMSG_AUTH_CONTINUED_SESSION
_headerBuffer.Resize(sizeof(ClientPktHeader));
uint32 accountId = uint32(key.Fields.AccountId);
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_CONTINUED_SESSION);
stmt->setUInt32(0, accountId);
_queryProcessor.AddQuery(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSocket::HandleAuthContinuedSessionCallback, this, authSession, std::placeholders::_1)));
}
void WorldSocket::HandleAuthContinuedSessionCallback(std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession, PreparedQueryResult result)
{
if (!result)
{
SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT);
DelayedCloseSocket();
return;
}
WorldSession::ConnectToKey key;
key.Raw = authSession->Key;
uint32 accountId = uint32(key.Fields.AccountId);
Field* fields = result->Fetch();
std::string login = fields[0].GetString();
BigNumber k;
k.SetHexStr(fields[1].GetCString());
_authCrypt.Init(&k, _encryptSeed.AsByteArray().get(), _decryptSeed.AsByteArray().get());
SHA1Hash sha;
sha.UpdateData(login);
sha.UpdateBigNumbers(&k, nullptr);
sha.UpdateData((uint8*)&_authSeed, 4);
sha.Finalize();
if (memcmp(sha.GetDigest(), authSession->Digest, sha.GetLength()))
{
SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT);
TC_LOG_ERROR("network", "WorldSocket::HandleAuthContinuedSession: Authentication failed for account: %u ('%s') address: %s", accountId, login.c_str(), GetRemoteIpAddress().to_string().c_str());
DelayedCloseSocket();
return;
}
sWorld->AddInstanceSocket(shared_from_this(), authSession->Key);
AsyncRead();
}
void WorldSocket::SendAuthResponseError(uint8 code)
{
WorldPacket packet(SMSG_AUTH_RESPONSE, 1);