TrinityCore
WorldSocket.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "WorldSocket.h"
21#include "CharacterPackets.h"
22#include "CryptoHash.h"
23#include "CryptoRandom.h"
24#include "DatabaseEnv.h"
25#include "Errors.h"
26#include "GameTime.h"
27#include "HMAC.h"
28#include "IPLocation.h"
29#include "PacketLog.h"
30#include "RealmList.h"
31#include "RBAC.h"
32#include "ScriptMgr.h"
33#include "SessionKeyGenerator.h"
34#include "World.h"
35#include "WorldPacket.h"
36#include "WorldSession.h"
37#include <zlib.h>
38
39#pragma pack(push, 1)
40
42{
46};
47
48#pragma pack(pop)
49
50std::string const WorldSocket::ServerConnectionInitialize("WORLD OF WARCRAFT CONNECTION - SERVER TO CLIENT - V2");
51std::string const WorldSocket::ClientConnectionInitialize("WORLD OF WARCRAFT CONNECTION - CLIENT TO SERVER - V2");
53
54uint8 const WorldSocket::AuthCheckSeed[16] = { 0xC5, 0xC6, 0x98, 0x95, 0x76, 0x3F, 0x1D, 0xCD, 0xB6, 0xA1, 0x37, 0x28, 0xB3, 0x12, 0xFF, 0x8A };
55uint8 const WorldSocket::SessionKeySeed[16] = { 0x58, 0xCB, 0xCF, 0x40, 0xFE, 0x2E, 0xCE, 0xA6, 0x5A, 0x90, 0xB8, 0x01, 0x68, 0x6C, 0x28, 0x0B };
56uint8 const WorldSocket::ContinuedSessionSeed[16] = { 0x16, 0xAD, 0x0C, 0xD4, 0x46, 0xF9, 0x4F, 0xB2, 0xEF, 0x7D, 0xEA, 0x2A, 0x17, 0x66, 0x4D, 0x2F };
57uint8 const WorldSocket::EncryptionKeySeed[16] = { 0xE9, 0x75, 0x3C, 0x50, 0x90, 0x93, 0x61, 0xDA, 0x3B, 0x07, 0xEE, 0xFA, 0xFF, 0x9D, 0x41, 0xB8 };
58
59WorldSocket::WorldSocket(boost::asio::ip::tcp::socket&& socket) : Socket(std::move(socket)),
60 _type(CONNECTION_TYPE_REALM), _key(0), _OverSpeedPings(0),
61 _worldSession(nullptr), _authed(false), _canRequestHotfixes(true), _sendBufferSize(4096), _compressionStream(nullptr)
62{
64 _sessionKey.fill(0);
65 _encryptKey.fill(0);
67}
68
70{
72 {
73 deflateEnd(_compressionStream);
74 delete _compressionStream;
75 }
76}
77
79{
80 std::string ip_address = GetRemoteIpAddress().to_string();
82 stmt->setString(0, ip_address);
83
84 _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback([self = shared_from_this()](PreparedQueryResult result)
85 {
86 self->CheckIpCallback(std::move(result));
87 }));
88}
89
91{
92 if (result)
93 {
94 bool banned = false;
95 do
96 {
97 Field* fields = result->Fetch();
98 if (fields[0].GetUInt64() != 0)
99 banned = true;
100
101 } while (result->NextRow());
102
103 if (banned)
104 {
105 TC_LOG_ERROR("network", "WorldSocket::CheckIpCallback: Sent Auth Response (IP {} banned).", GetRemoteIpAddress().to_string());
107 return;
108 }
109 }
110
112
114
115 MessageBuffer initializer;
116 initializer.Write(ServerConnectionInitialize.c_str(), ServerConnectionInitialize.length());
117 initializer.Write("\n", 1);
118
119 // - IoContext.run thread, safe.
120 QueuePacket(std::move(initializer));
121}
122
123void WorldSocket::InitializeHandler(boost::system::error_code const& error, std::size_t transferedBytes)
124{
125 if (error)
126 {
127 CloseSocket();
128 return;
129 }
130
131 GetReadBuffer().WriteCompleted(transferedBytes);
132
133 MessageBuffer& packet = GetReadBuffer();
134 if (packet.GetActiveSize() > 0)
135 {
137 {
138 // need to receive the header
139 std::size_t readHeaderSize = std::min(packet.GetActiveSize(), _packetBuffer.GetRemainingSpace());
140 _packetBuffer.Write(packet.GetReadPointer(), readHeaderSize);
141 packet.ReadCompleted(readHeaderSize);
142
144 {
145 // Couldn't receive the whole header this time.
146 ASSERT(packet.GetActiveSize() == 0);
148 return;
149 }
150
151 try
152 {
153 ByteBuffer buffer(std::move(_packetBuffer));
154 std::string initializer = buffer.ReadString(ClientConnectionInitialize.length());
155 if (initializer != ClientConnectionInitialize)
156 {
157 CloseSocket();
158 return;
159 }
160
161 uint8 terminator;
162 buffer >> terminator;
163 if (terminator != '\n')
164 {
165 CloseSocket();
166 return;
167 }
168 }
169 catch (ByteBufferException const& ex)
170 {
171 TC_LOG_ERROR("network", "WorldSocket::InitializeHandler ByteBufferException {} occured while parsing initial packet from {}",
172 ex.what(), GetRemoteIpAddress().to_string());
173 CloseSocket();
174 return;
175 }
176
178 _compressionStream->zalloc = (alloc_func)nullptr;
179 _compressionStream->zfree = (free_func)nullptr;
180 _compressionStream->opaque = (voidpf)nullptr;
181 _compressionStream->avail_in = 0;
182 _compressionStream->next_in = nullptr;
183 int32 z_res = deflateInit2(_compressionStream, sWorld->getIntConfig(CONFIG_COMPRESSION), Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
184 if (z_res != Z_OK)
185 {
186 CloseSocket();
187 TC_LOG_ERROR("network", "Can't initialize packet compression (zlib: deflateInit) Error code: {} ({})", z_res, zError(z_res));
188 return;
189 }
190
193 AsyncRead();
194 return;
195 }
196 }
197
199}
200
202{
203 EncryptablePacket* queued;
205 while (_bufferQueue.Dequeue(queued))
206 {
207 uint32 packetSize = queued->size() + 2 /*opcode*/;
208 if (packetSize > MinSizeForCompression && queued->NeedsEncryption())
209 packetSize = deflateBound(_compressionStream, packetSize) + sizeof(CompressedWorldPacket);
210
211 // Flush current buffer if too small for next packet
212 if (buffer.GetRemainingSpace() < packetSize + sizeof(PacketHeader))
213 {
214 QueuePacket(std::move(buffer));
215 buffer.Resize(_sendBufferSize);
216 }
217
218 if (buffer.GetRemainingSpace() >= packetSize + sizeof(PacketHeader))
219 WritePacketToBuffer(*queued, buffer);
220 else // single packet larger than _sendBufferSize
221 {
222 MessageBuffer packetBuffer(packetSize + sizeof(PacketHeader));
223 WritePacketToBuffer(*queued, packetBuffer);
224 QueuePacket(std::move(packetBuffer));
225 }
226
227 delete queued;
228 }
229
230 if (buffer.GetActiveSize() > 0)
231 QueuePacket(std::move(buffer));
232
233 if (!BaseSocket::Update())
234 return false;
235
237
238 return true;
239}
240
242{
244 challenge.Challenge = _serverChallenge;
245 memcpy(challenge.DosChallenge.data(), Trinity::Crypto::GetRandomBytes<32>().data(), 32);
246 challenge.DosZeroBits = 1;
247
248 SendPacketAndLogOpcode(*challenge.Write());
249}
250
252{
253 {
254 std::lock_guard<std::mutex> sessionGuard(_worldSessionLock);
255 _worldSession = nullptr;
256 }
257}
258
260{
261 if (!IsOpen())
262 return;
263
264 MessageBuffer& packet = GetReadBuffer();
265 while (packet.GetActiveSize() > 0)
266 {
268 {
269 // need to receive the header
270 std::size_t readHeaderSize = std::min(packet.GetActiveSize(), _headerBuffer.GetRemainingSpace());
271 _headerBuffer.Write(packet.GetReadPointer(), readHeaderSize);
272 packet.ReadCompleted(readHeaderSize);
273
275 {
276 // Couldn't receive the whole header this time.
277 ASSERT(packet.GetActiveSize() == 0);
278 break;
279 }
280
281 // We just received nice new header
282 if (!ReadHeaderHandler())
283 {
284 CloseSocket();
285 return;
286 }
287 }
288
289 // We have full read header, now check the data payload
291 {
292 // need more data in the payload
293 std::size_t readDataSize = std::min(packet.GetActiveSize(), _packetBuffer.GetRemainingSpace());
294 _packetBuffer.Write(packet.GetReadPointer(), readDataSize);
295 packet.ReadCompleted(readDataSize);
296
298 {
299 // Couldn't receive the whole data this time.
300 ASSERT(packet.GetActiveSize() == 0);
301 break;
302 }
303 }
304
305 // just received fresh new payload
308 if (result != ReadDataHandlerResult::Ok)
309 {
311 CloseSocket();
312
313 return;
314 }
315 }
316
317 AsyncRead();
318}
319
321{
322 std::lock_guard<std::mutex> sessionGuard(_worldSessionLock);
323 _worldSession = session;
324 _authed = true;
325}
326
328{
329 ASSERT(_headerBuffer.GetActiveSize() == sizeof(IncomingPacketHeader), "Header size " SZFMTD " different than expected " SZFMTD, _headerBuffer.GetActiveSize(), sizeof(IncomingPacketHeader));
330
332 uint16 encryptedOpcode = header->EncryptedOpcode;
333
334 if (!header->IsValidSize())
335 {
336 _authCrypt.PeekDecryptRecv(reinterpret_cast<uint8*>(&header->EncryptedOpcode), sizeof(encryptedOpcode));
337
338 // CMSG_HOTFIX_REQUEST can be much larger than normal packets, allow receiving it once per session
339 if (header->EncryptedOpcode != CMSG_HOTFIX_REQUEST || header->Size > 0x100000 || !_canRequestHotfixes)
340 {
341 TC_LOG_ERROR("network", "WorldSocket::ReadHeaderHandler(): client {} sent malformed packet (size: {}, opcode {})",
342 GetRemoteIpAddress().to_string(), header->Size, uint32(header->EncryptedOpcode));
343 return false;
344 }
345 }
346
347 _packetBuffer.Resize(header->Size);
348 _packetBuffer.Write(&encryptedOpcode, sizeof(encryptedOpcode));
349 return true;
350}
351
353{
354 PacketHeader* header = reinterpret_cast<PacketHeader*>(_headerBuffer.GetReadPointer());
355
356 if (!_authCrypt.DecryptRecv(_packetBuffer.GetReadPointer(), header->Size, header->Tag))
357 {
358 TC_LOG_ERROR("network", "WorldSocket::ReadHeaderHandler(): client {} failed to decrypt packet (size: {})",
359 GetRemoteIpAddress().to_string(), header->Size);
361 }
362
363 WorldPacket packet(std::move(_packetBuffer), GetConnectionType());
364 OpcodeClient opcode = packet.read<OpcodeClient>();
365 if (uint32(opcode) >= uint32(NUM_OPCODE_HANDLERS))
366 {
367 TC_LOG_ERROR("network", "WorldSocket::ReadHeaderHandler(): client {} sent wrong opcode (opcode: {})",
368 GetRemoteIpAddress().to_string(), uint32(opcode));
370 }
371
372 packet.SetOpcode(opcode);
373
374 if (sPacketLog->CanLogPacket())
376
377 std::unique_lock<std::mutex> sessionGuard(_worldSessionLock, std::defer_lock);
378
379 switch (opcode)
380 {
381 case CMSG_PING:
382 {
383 LogOpcodeText(opcode, sessionGuard);
384 WorldPackets::Auth::Ping ping(std::move(packet));
385 if (!ping.ReadNoThrow())
386 {
387 TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client {} sent malformed CMSG_PING", GetRemoteIpAddress().to_string());
389 }
390 if (!HandlePing(ping))
392 break;
393 }
395 {
396 LogOpcodeText(opcode, sessionGuard);
397 if (_authed)
398 {
399 // locking just to safely log offending user is probably overkill but we are disconnecting him anyway
400 if (sessionGuard.try_lock())
401 TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from {}", _worldSession->GetPlayerInfo());
403 }
404
405 std::shared_ptr<WorldPackets::Auth::AuthSession> authSession = std::make_shared<WorldPackets::Auth::AuthSession>(std::move(packet));
406 if (!authSession->ReadNoThrow())
407 {
408 TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client {} sent malformed CMSG_AUTH_SESSION", GetRemoteIpAddress().to_string());
410 }
411 HandleAuthSession(authSession);
413 }
415 {
416 LogOpcodeText(opcode, sessionGuard);
417 if (_authed)
418 {
419 // locking just to safely log offending user is probably overkill but we are disconnecting him anyway
420 if (sessionGuard.try_lock())
421 TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_CONTINUED_SESSION from {}", _worldSession->GetPlayerInfo());
423 }
424
425 std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession = std::make_shared<WorldPackets::Auth::AuthContinuedSession>(std::move(packet));
426 if (!authSession->ReadNoThrow())
427 {
428 TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client {} sent malformed CMSG_AUTH_CONTINUED_SESSION", GetRemoteIpAddress().to_string());
430 }
431 HandleAuthContinuedSession(authSession);
433 }
434 case CMSG_KEEP_ALIVE:
435 sessionGuard.lock();
436 LogOpcodeText(opcode, sessionGuard);
437 if (_worldSession)
438 {
441 }
442 TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler: client {} sent CMSG_KEEP_ALIVE without being authenticated", GetRemoteIpAddress().to_string());
445 LogOpcodeText(opcode, sessionGuard);
446 packet.rfinish(); // contains uint32 disconnectReason;
447 break;
449 LogOpcodeText(opcode, sessionGuard);
450 SetNoDelay(false);
451 break;
453 {
454 sessionGuard.lock();
455
456 LogOpcodeText(opcode, sessionGuard);
457 WorldPackets::Auth::ConnectToFailed connectToFailed(std::move(packet));
458 if (!connectToFailed.ReadNoThrow())
459 {
460 TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client {} sent malformed CMSG_CONNECT_TO_FAILED", GetRemoteIpAddress().to_string());
462 }
463 HandleConnectToFailed(connectToFailed);
464 break;
465 }
467 LogOpcodeText(opcode, sessionGuard);
469 break;
471 _canRequestHotfixes = false;
472 [[fallthrough]];
473 default:
474 {
475 if (opcode == CMSG_TIME_SYNC_RESPONSE)
476 packet.SetReceiveTime(std::chrono::steady_clock::now());
477
478 sessionGuard.lock();
479
480 LogOpcodeText(opcode, sessionGuard);
481
482 if (!_worldSession)
483 {
484 TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = {}", uint32(opcode));
486 }
487
488 OpcodeHandler const* handler = opcodeTable[opcode];
489 if (!handler)
490 {
491 TC_LOG_ERROR("network.opcode", "No defined handler for opcode {} sent by {}", GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet.GetOpcode())), _worldSession->GetPlayerInfo());
492 break;
493 }
494
495 // Our Idle timer will reset on any non PING opcodes on login screen, allowing us to catch people idling.
497
498 // Copy the packet to the heap before enqueuing
499 _worldSession->QueuePacket(new WorldPacket(std::move(packet)));
500 break;
501 }
502 }
503
505}
506
507void WorldSocket::LogOpcodeText(OpcodeClient opcode, std::unique_lock<std::mutex> const& guard) const
508{
509 if (!guard)
510 {
511 TC_LOG_TRACE("network.opcode", "C->S: {} {}", GetRemoteIpAddress().to_string(), GetOpcodeNameForLogging(opcode));
512 }
513 else
514 {
515 TC_LOG_TRACE("network.opcode", "C->S: {} {}", (_worldSession ? _worldSession->GetPlayerInfo() : GetRemoteIpAddress().to_string()),
517 }
518}
519
521{
522 TC_LOG_TRACE("network.opcode", "S->C: {} {}", GetRemoteIpAddress().to_string(), GetOpcodeNameForLogging(static_cast<OpcodeServer>(packet.GetOpcode())));
523 SendPacket(packet);
524}
525
527{
528 if (!IsOpen())
529 return;
530
531 if (sPacketLog->CanLogPacket())
533
535}
536
538{
539 uint16 opcode = packet.GetOpcode();
540 uint32 packetSize = packet.size();
541
542 // Reserve space for buffer
543 uint8* headerPos = buffer.GetWritePointer();
544 buffer.WriteCompleted(sizeof(PacketHeader));
545 uint8* dataPos = buffer.GetWritePointer();
546 buffer.WriteCompleted(sizeof(opcode));
547
548 if (packetSize > MinSizeForCompression && packet.NeedsEncryption())
549 {
551 cmp.UncompressedSize = packetSize + 2;
552 cmp.UncompressedAdler = adler32(adler32(0x9827D8F1, (Bytef*)&opcode, 2), packet.contents(), packetSize);
553
554 // Reserve space for compression info - uncompressed size and checksums
555 uint8* compressionInfo = buffer.GetWritePointer();
557
558 uint32 compressedSize = CompressPacket(buffer.GetWritePointer(), packet);
559
560 cmp.CompressedAdler = adler32(0x9827D8F1, buffer.GetWritePointer(), compressedSize);
561
562 memcpy(compressionInfo, &cmp, sizeof(CompressedWorldPacket));
563 buffer.WriteCompleted(compressedSize);
564 packetSize = compressedSize + sizeof(CompressedWorldPacket);
565
566 opcode = SMSG_COMPRESSED_PACKET;
567 }
568 else if (!packet.empty())
569 buffer.Write(packet.contents(), packet.size());
570
571 memcpy(dataPos, &opcode, sizeof(opcode));
572 packetSize += 2 /*opcode*/;
573
574 PacketHeader header;
575 header.Size = packetSize;
576 _authCrypt.EncryptSend(dataPos, header.Size, header.Tag);
577
578 memcpy(headerPos, &header, sizeof(PacketHeader));
579}
580
582{
583 uint32 opcode = packet.GetOpcode();
584 uint32 bufferSize = deflateBound(_compressionStream, packet.size() + sizeof(uint16));
585
586 _compressionStream->next_out = buffer;
587 _compressionStream->avail_out = bufferSize;
588 _compressionStream->next_in = (Bytef*)&opcode;
589 _compressionStream->avail_in = sizeof(uint16);
590
591 int32 z_res = deflate(_compressionStream, Z_NO_FLUSH);
592 if (z_res != Z_OK)
593 {
594 TC_LOG_ERROR("network", "Can't compress packet opcode (zlib: deflate) Error code: {} ({}, msg: {})", z_res, zError(z_res), _compressionStream->msg);
595 return 0;
596 }
597
598 _compressionStream->next_in = (Bytef*)packet.contents();
599 _compressionStream->avail_in = packet.size();
600
601 z_res = deflate(_compressionStream, Z_SYNC_FLUSH);
602 if (z_res != Z_OK)
603 {
604 TC_LOG_ERROR("network", "Can't compress packet data (zlib: deflate) Error code: {} ({}, msg: {})", z_res, zError(z_res), _compressionStream->msg);
605 return 0;
606 }
607
608 return bufferSize - _compressionStream->avail_out;
609}
610
612{
613 struct
614 {
617 std::string LastIP;
618 std::string LockCountry;
621
623
624 struct
625 {
626 uint32 Id;
627 std::array<uint8, 64> KeyData;
631 std::string OS;
635 bool IsBanned;
637
638 bool IsBanned() const { return BattleNet.IsBanned || Game.IsBanned; }
639
640 explicit AccountInfo(Field const* fields)
641 {
642 // 0 1 2 3 4 5 6 7 8 9 10 11 12
643 // SELECT a.id, a.session_key, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, ba.locale, a.recruiter, a.os, a.timezone_offset, ba.id, aa.SecurityLevel,
644 // 13 14 15
645 // bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id
646 // FROM account a LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id LEFT JOIN account_access aa ON a.id = aa.AccountID AND aa.RealmID IN (-1, ?)
647 // 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
648 // WHERE a.username = ? AND LENGTH(a.session_key) = 40 ORDER BY aa.RealmID DESC LIMIT 1
649 Game.Id = fields[0].GetUInt32();
650 Game.KeyData = fields[1].GetBinary<64>();
651 BattleNet.LastIP = fields[2].GetString();
652 BattleNet.IsLockedToIP = fields[3].GetBool();
653 BattleNet.LockCountry = fields[4].GetString();
654 Game.Expansion = fields[5].GetUInt8();
655 Game.MuteTime = fields[6].GetInt64();
656 BattleNet.Locale = LocaleConstant(fields[7].GetUInt8());
657 Game.Recruiter = fields[8].GetUInt32();
658 Game.OS = fields[9].GetString();
659 Game.TimezoneOffset = Minutes(fields[10].GetInt16());
660 BattleNet.Id = fields[11].GetUInt32();
661 Game.Security = AccountTypes(fields[12].GetUInt8());
662 BattleNet.IsBanned = fields[13].GetUInt32() != 0;
663 Game.IsBanned = fields[14].GetUInt32() != 0;
664 Game.IsRectuiter = fields[15].GetUInt32() != 0;
665
666 if (BattleNet.Locale >= TOTAL_LOCALES)
667 BattleNet.Locale = LOCALE_enUS;
668 }
669};
670
671void WorldSocket::HandleAuthSession(std::shared_ptr<WorldPackets::Auth::AuthSession> authSession)
672{
673 // Get the account information from the auth database
675 stmt->setInt32(0, int32(realm.Id.Realm));
676 stmt->setString(1, authSession->RealmJoinTicket);
677
678 _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback([this, authSession = std::move(authSession)](PreparedQueryResult result) mutable
679 {
680 HandleAuthSessionCallback(std::move(authSession), std::move(result));
681 }));
682}
683
684void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::AuthSession> authSession, PreparedQueryResult result)
685{
686 // Stop if the account is not found
687 if (!result)
688 {
689 // We can not log here, as we do not know the account. Thus, no accountId.
690 TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account).");
692 return;
693 }
694
695 RealmBuildInfo const* buildInfo = sRealmList->GetBuildInfo(realm.Build);
696 if (!buildInfo)
697 {
699 TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Missing auth seed for realm build {} ({}).", realm.Build, GetRemoteIpAddress().to_string());
701 return;
702 }
703
704 AccountInfo account(result->Fetch());
705
706 // For hook purposes, we get Remoteaddress at this point.
707 std::string address = GetRemoteIpAddress().to_string();
708
709 Trinity::Crypto::SHA256 digestKeyHash;
710 digestKeyHash.UpdateData(account.Game.KeyData.data(), account.Game.KeyData.size());
711 if (account.Game.OS == "Wn64")
712 digestKeyHash.UpdateData(buildInfo->Win64AuthSeed.data(), buildInfo->Win64AuthSeed.size());
713 else if (account.Game.OS == "Mc64")
714 digestKeyHash.UpdateData(buildInfo->Mac64AuthSeed.data(), buildInfo->Mac64AuthSeed.size());
715
716 digestKeyHash.Finalize();
717
718 Trinity::Crypto::HMAC_SHA256 hmac(digestKeyHash.GetDigest());
719 hmac.UpdateData(authSession->LocalChallenge);
721 hmac.UpdateData(AuthCheckSeed, 16);
722 hmac.Finalize();
723
724 // Check that Key and account name are the same on client and server
725 if (memcmp(hmac.GetDigest().data(), authSession->Digest.data(), authSession->Digest.size()) != 0)
726 {
727 TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: {} ('{}') address: {}", account.Game.Id, authSession->RealmJoinTicket, address);
729 return;
730 }
731
733 keyData.UpdateData(account.Game.KeyData.data(), account.Game.KeyData.size());
734 keyData.Finalize();
735
736 Trinity::Crypto::HMAC_SHA256 sessionKeyHmac(keyData.GetDigest());
737 sessionKeyHmac.UpdateData(_serverChallenge);
738 sessionKeyHmac.UpdateData(authSession->LocalChallenge);
739 sessionKeyHmac.UpdateData(SessionKeySeed, 16);
740 sessionKeyHmac.Finalize();
741
742 SessionKeyGenerator<Trinity::Crypto::SHA256> sessionKeyGenerator(sessionKeyHmac.GetDigest());
743 sessionKeyGenerator.Generate(_sessionKey.data(), 40);
744
746 encryptKeyGen.UpdateData(authSession->LocalChallenge);
747 encryptKeyGen.UpdateData(_serverChallenge);
748 encryptKeyGen.UpdateData(EncryptionKeySeed, 16);
749 encryptKeyGen.Finalize();
750
751 // only first 16 bytes of the hmac are used
752 memcpy(_encryptKey.data(), encryptKeyGen.GetDigest().data(), 16);
753
754 LoginDatabasePreparedStatement* stmt = nullptr;
755
757 {
758 // As we don't know if attempted login process by ip works, we update last_attempt_ip right away
759 stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP);
760 stmt->setString(0, address);
761 stmt->setString(1, authSession->RealmJoinTicket);
762 LoginDatabase.Execute(stmt);
763 // This also allows to check for possible "hack" attempts on account
764 }
765
766 stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_INFO_CONTINUED_SESSION);
767 stmt->setBinary(0, _sessionKey);
768 stmt->setUInt32(1, account.Game.Id);
769 LoginDatabase.Execute(stmt);
770
771 // First reject the connection if packet contains invalid data or realm state doesn't allow logging in
772 if (sWorld->IsClosed())
773 {
775 TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: World closed, denying client ({}).", GetRemoteIpAddress().to_string());
777 return;
778 }
779
780 if (authSession->RealmID != realm.Id.Realm)
781 {
783 TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client {} requested connecting with realm id {} but this realm has id {} set in config.",
784 GetRemoteIpAddress().to_string(), authSession->RealmID, realm.Id.Realm);
786 return;
787 }
788
789 // Must be done before WorldSession is created
790 bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED);
791 if (wardenActive && account.Game.OS != "Win" && account.Game.OS != "Wn64" && account.Game.OS != "Mc64")
792 {
794 TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client {} attempted to log in using invalid client OS ({}).", address, account.Game.OS);
796 return;
797 }
798
799 if (IpLocationRecord const* location = sIPLocation->GetLocationRecord(address))
800 _ipCountry = location->CountryCode;
801
803 if (account.BattleNet.IsLockedToIP)
804 {
805 if (account.BattleNet.LastIP != address)
806 {
808 TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: {}, new IP: {}).", account.BattleNet.LastIP, address);
809 // We could log on hook only instead of an additional db log, however action logger is config based. Better keep DB logging as well
810 sScriptMgr->OnFailedAccountLogin(account.Game.Id);
812 return;
813 }
814 }
815 else if (!account.BattleNet.LockCountry.empty() && account.BattleNet.LockCountry != "00" && !_ipCountry.empty())
816 {
817 if (account.BattleNet.LockCountry != _ipCountry)
818 {
820 TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account country differs. Original country: {}, new country: {}).", account.BattleNet.LockCountry, _ipCountry);
821 // We could log on hook only instead of an additional db log, however action logger is config based. Better keep DB logging as well
822 sScriptMgr->OnFailedAccountLogin(account.Game.Id);
824 return;
825 }
826 }
827
828 int64 mutetime = account.Game.MuteTime;
830 if (mutetime < 0)
831 {
832 mutetime = GameTime::GetGameTime() + std::llabs(mutetime);
833
834 stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME_LOGIN);
835 stmt->setInt64(0, mutetime);
836 stmt->setUInt32(1, account.Game.Id);
837 LoginDatabase.Execute(stmt);
838 }
839
840 if (account.IsBanned())
841 {
843 TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned).");
844 sScriptMgr->OnFailedAccountLogin(account.Game.Id);
846 return;
847 }
848
849 // Check locked state for server
850 AccountTypes allowedAccountType = sWorld->GetPlayerSecurityLimit();
851 TC_LOG_DEBUG("network", "Allowed Level: {} Player Level {}", allowedAccountType, account.Game.Security);
852 if (allowedAccountType > SEC_PLAYER && account.Game.Security < allowedAccountType)
853 {
855 TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough");
856 sScriptMgr->OnFailedAccountLogin(account.Game.Id);
858 return;
859 }
860
861 TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '{}' authenticated successfully from {}.", authSession->RealmJoinTicket, address);
862
864 {
865 // Update the last_ip in the database as it was successful for login
866 stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_IP);
867
868 stmt->setString(0, address);
869 stmt->setString(1, authSession->RealmJoinTicket);
870
871 LoginDatabase.Execute(stmt);
872 }
873
874 // At this point, we can safely hook a successful login
875 sScriptMgr->OnAccountLogin(account.Game.Id);
876
877 _authed = true;
878 _worldSession = new WorldSession(account.Game.Id, std::move(authSession->RealmJoinTicket), account.BattleNet.Id, shared_from_this(), account.Game.Security,
879 account.Game.Expansion, mutetime, account.Game.OS, account.Game.TimezoneOffset, account.BattleNet.Locale, account.Game.Recruiter, account.Game.IsRectuiter);
880
881 // Initialize Warden system only if it is enabled by config
882 if (wardenActive)
884
886 {
887 LoadSessionPermissionsCallback(std::move(result));
888 }));
889 AsyncRead();
890}
891
893{
894 // RBAC must be loaded before adding session to check for skip queue permission
896
898}
899
900void WorldSocket::HandleAuthContinuedSession(std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession)
901{
903 key.Raw = authSession->Key;
904
907 {
910 return;
911 }
912
913 uint32 accountId = uint32(key.Fields.AccountId);
915 stmt->setUInt32(0, accountId);
916
917 _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback([this, authSession = std::move(authSession)](PreparedQueryResult result) mutable
918 {
919 HandleAuthContinuedSessionCallback(std::move(authSession), std::move(result));
920 }));
921}
922
923void WorldSocket::HandleAuthContinuedSessionCallback(std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession, PreparedQueryResult result)
924{
925 if (!result)
926 {
929 return;
930 }
931
933 _key = key.Raw = authSession->Key;
934
935 uint32 accountId = uint32(key.Fields.AccountId);
936 Field* fields = result->Fetch();
937 std::string login = fields[0].GetString();
939
941 hmac.UpdateData(reinterpret_cast<uint8 const*>(&authSession->Key), sizeof(authSession->Key));
942 hmac.UpdateData(authSession->LocalChallenge);
943 hmac.UpdateData(_serverChallenge);
944 hmac.UpdateData(ContinuedSessionSeed, 16);
945 hmac.Finalize();
946
947 if (memcmp(hmac.GetDigest().data(), authSession->Digest.data(), authSession->Digest.size()))
948 {
949 TC_LOG_ERROR("network", "WorldSocket::HandleAuthContinuedSession: Authentication failed for account: {} ('{}') address: {}", accountId, login, GetRemoteIpAddress().to_string());
951 return;
952 }
953
955 encryptKeyGen.UpdateData(authSession->LocalChallenge);
956 encryptKeyGen.UpdateData(_serverChallenge);
957 encryptKeyGen.UpdateData(EncryptionKeySeed, 16);
958 encryptKeyGen.Finalize();
959
960 // only first 16 bytes of the hmac are used
961 memcpy(_encryptKey.data(), encryptKeyGen.GetDigest().data(), 16);
962
964 AsyncRead();
965}
966
968{
969 if (_worldSession)
970 {
972 {
973 switch (connectToFailed.Serial)
974 {
977 break;
980 break;
983 break;
986 break;
988 {
989 TC_LOG_ERROR("network", "{} failed to connect 5 times to world socket, aborting login", _worldSession->GetPlayerInfo());
991 break;
992 }
993 default:
994 return;
995 }
996 }
997 //else
998 //{
999 // transfer_aborted when/if we get map node redirection
1000 // SendPacketAndLogOpcode(*WorldPackets::Auth::ResumeComms().Write());
1001 //}
1002 }
1003}
1004
1006{
1009 sWorld->AddSession(_worldSession);
1010 else
1011 sWorld->AddInstanceSocket(shared_from_this(), _key);
1012}
1013
1015{
1017 response.Result = code;
1018 SendPacketAndLogOpcode(*response.Write());
1019}
1020
1022{
1023 using namespace std::chrono;
1024
1025 if (_LastPingTime == steady_clock::time_point())
1026 {
1027 _LastPingTime = steady_clock::now();
1028 }
1029 else
1030 {
1031 steady_clock::time_point now = steady_clock::now();
1032
1033 steady_clock::duration diff = now - _LastPingTime;
1034
1035 _LastPingTime = now;
1036
1037 if (diff < seconds(27))
1038 {
1040
1041 uint32 maxAllowed = sWorld->getIntConfig(CONFIG_MAX_OVERSPEED_PINGS);
1042
1043 if (maxAllowed && _OverSpeedPings > maxAllowed)
1044 {
1045 std::unique_lock<std::mutex> sessionGuard(_worldSessionLock);
1046
1048 {
1049 TC_LOG_ERROR("network", "WorldSocket::HandlePing: {} kicked for over-speed pings (address: {})",
1051
1052 return false;
1053 }
1054 }
1055 }
1056 else
1057 _OverSpeedPings = 0;
1058 }
1059
1060 {
1061 std::lock_guard<std::mutex> sessionGuard(_worldSessionLock);
1062
1063 if (_worldSession)
1065 else
1066 {
1067 TC_LOG_ERROR("network", "WorldSocket::HandlePing: peer sent CMSG_PING, but is not authenticated or got recently kicked, address = {}", GetRemoteIpAddress().to_string());
1068 return false;
1069 }
1070 }
1071
1073 return true;
1074}
constexpr size_t SESSION_KEY_LENGTH
Definition: AuthDefines.h:24
@ ERROR_GAME_ACCOUNT_BANNED
@ ERROR_RISK_ACCOUNT_LOCKED
@ ERROR_BAD_VERSION
@ ERROR_DENIED
@ ERROR_SERVER_IS_PRIVATE
LocaleConstant
Definition: Common.h:48
@ TOTAL_LOCALES
Definition: Common.h:62
@ LOCALE_enUS
Definition: Common.h:49
AccountTypes
Definition: Common.h:39
@ SEC_PLAYER
Definition: Common.h:40
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabase
Accessor to the realm/login database.
Definition: DatabaseEnv.cpp:22
uint8_t uint8
Definition: Define.h:144
int64_t int64
Definition: Define.h:137
int32_t int32
Definition: Define.h:138
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
#define SZFMTD
Definition: Define.h:132
std::chrono::minutes Minutes
Minutes shorthand typedef.
Definition: Duration.h:35
#define ASSERT
Definition: Errors.h:68
#define sIPLocation
Definition: IPLocation.h:51
#define TC_LOG_DEBUG(filterType__,...)
Definition: Log.h:156
#define TC_LOG_TRACE(filterType__,...)
Definition: Log.h:153
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
@ LOGIN_UPD_MUTE_TIME_LOGIN
Definition: LoginDatabase.h:68
@ LOGIN_UPD_ACCOUNT_INFO_CONTINUED_SESSION
Definition: LoginDatabase.h:40
@ LOGIN_UPD_LAST_ATTEMPT_IP
Definition: LoginDatabase.h:70
@ LOGIN_UPD_LAST_IP
Definition: LoginDatabase.h:69
@ LOGIN_SEL_IP_INFO
Definition: LoginDatabase.h:34
@ LOGIN_SEL_ACCOUNT_INFO_CONTINUED_SESSION
Definition: LoginDatabase.h:41
@ LOGIN_SEL_ACCOUNT_INFO_BY_NAME
Definition: LoginDatabase.h:45
#define sPacketLog
Definition: PacketLog.h:63
@ SERVER_TO_CLIENT
Definition: PacketLog.h:27
@ CLIENT_TO_SERVER
Definition: PacketLog.h:26
const size_t bufferSize
Definition: RASession.h:27
Role Based Access Control related classes definition.
#define sRealmList
Definition: RealmList.h:96
#define sScriptMgr
Definition: ScriptMgr.h:1418
struct z_stream_s z_stream
Definition: WorldSocket.h:33
char const * what() const noexcept override
Definition: ByteBuffer.h:36
void rfinish()
Definition: ByteBuffer.h:407
size_t size() const
Definition: ByteBuffer.h:536
bool empty() const
Definition: ByteBuffer.h:537
uint8 * contents()
Definition: ByteBuffer.h:522
std::string ReadString(uint32 length, bool requireValidUtf8=true)
Definition: ByteBuffer.cpp:78
bool NeedsEncryption() const
Definition: WorldSocket.h:48
Class used to access individual fields of database query result.
Definition: Field.h:90
uint8 GetUInt8() const
Definition: Field.cpp:30
std::string GetString() const
Definition: Field.cpp:118
std::vector< uint8 > GetBinary() const
Definition: Field.cpp:142
int64 GetInt64() const
Definition: Field.cpp:86
bool GetBool() const
Definition: Field.h:98
uint32 GetUInt32() const
Definition: Field.cpp:62
void Resize(size_type bytes)
Definition: MessageBuffer.h:52
size_type GetRemainingSpace() const
Definition: MessageBuffer.h:69
void ReadCompleted(size_type bytes)
Definition: MessageBuffer.h:63
void WriteCompleted(size_type bytes)
Definition: MessageBuffer.h:65
uint8 * GetReadPointer()
Definition: MessageBuffer.h:59
size_type GetActiveSize() const
Definition: MessageBuffer.h:67
uint8 * GetWritePointer()
Definition: MessageBuffer.h:61
void Write(void const *data, std::size_t size)
Definition: MessageBuffer.h:93
void setInt32(const uint8 index, const int32 value)
void setInt64(const uint8 index, const int64 value)
void setBinary(const uint8 index, const std::vector< uint8 > &value)
void setUInt32(const uint8 index, const uint32 value)
void setString(const uint8 index, const std::string &value)
QueryCallback && WithPreparedCallback(std::function< void(PreparedQueryResult)> &&callback)
void Generate(uint8 *buf, uint32 sz)
Definition: Socket.h:63
MessageBuffer & GetReadBuffer()
Definition: Socket.h:169
virtual bool Update()
Definition: Socket.h:87
void AsyncReadWithCallback(void(T::*callback)(boost::system::error_code const &, std::size_t))
Definition: Socket.h:127
void DelayedCloseSocket()
Marks the socket for closing after write buffer becomes empty.
Definition: Socket.h:167
boost::asio::ip::address GetRemoteIpAddress() const
Definition: Socket.h:103
void QueuePacket(MessageBuffer &&buffer)
Definition: Socket.h:141
bool IsOpen() const
Definition: Socket.h:150
void SetNoDelay(bool enable)
Definition: Socket.h:201
uint16 GetRemotePort() const
Definition: Socket.h:108
void UpdateData(uint8 const *data, size_t len)
Definition: HMAC.h:108
Digest const & GetDigest() const
Definition: HMAC.h:127
void UpdateData(uint8 const *data, size_t len)
Definition: CryptoHash.h:111
Digest const & GetDigest() const
Definition: CryptoHash.h:130
bool IsInitialized() const
bool PeekDecryptRecv(uint8 *data, size_t length)
void Init(Trinity::Crypto::AES::Key const &key)
bool EncryptSend(uint8 *data, size_t length, Trinity::Crypto::AES::Tag &tag)
bool DecryptRecv(uint8 *data, size_t length, Trinity::Crypto::AES::Tag &tag)
void SetReceiveTime(TimePoint receivedTime)
Definition: WorldPacket.h:90
uint32 GetOpcode() const
Definition: WorldPacket.h:84
void SetOpcode(uint32 opcode)
Definition: WorldPacket.h:85
WorldPacket const * Write() override
uint32 Result
the result of the authentication process, possible values are BattlenetRpcErrorCode
WorldPacket const * Write() override
WorldPacket const * Write() override
Player session in the World.
Definition: WorldSession.h:963
void AbortLogin(WorldPackets::Character::LoginFailureReason reason)
void SendConnectToInstance(WorldPackets::Auth::ConnectToSerial serial)
void QueuePacket(WorldPacket *new_packet)
Add an incoming packet to the queue.
void InitWarden(SessionKey const &k)
std::string GetPlayerInfo() const
bool PlayerLoading() const
Definition: WorldSession.h:969
QueryCallback LoadPermissionsAsync()
bool HasPermission(uint32 permissionId)
void ResetTimeOutTime(bool onlyActive)
void SetLatency(uint32 latency)
rbac::RBACData * GetRBACData()
SessionKey _sessionKey
Definition: WorldSocket.h:156
void CheckIpCallback(PreparedQueryResult result)
Definition: WorldSocket.cpp:90
ConnectionType _type
Definition: WorldSocket.h:151
MessageBuffer _headerBuffer
Definition: WorldSocket.h:167
void HandleAuthContinuedSessionCallback(std::shared_ptr< WorldPackets::Auth::AuthContinuedSession > authSession, PreparedQueryResult result)
void HandleAuthContinuedSession(std::shared_ptr< WorldPackets::Auth::AuthContinuedSession > authSession)
void LogOpcodeText(OpcodeClient opcode, std::unique_lock< std::mutex > const &guard) const
void InitializeHandler(boost::system::error_code const &error, std::size_t transferedBytes)
static uint8 const SessionKeySeed[16]
Definition: WorldSocket.h:92
ConnectionType GetConnectionType() const
Definition: WorldSocket.h:110
static std::string const ClientConnectionInitialize
Definition: WorldSocket.h:88
void HandleConnectToFailed(WorldPackets::Auth::ConnectToFailed &connectToFailed)
bool ReadHeaderHandler()
static uint8 const ContinuedSessionSeed[16]
Definition: WorldSocket.h:93
bool _canRequestHotfixes
Definition: WorldSocket.h:165
bool Update() override
void HandleAuthSessionCallback(std::shared_ptr< WorldPackets::Auth::AuthSession > authSession, PreparedQueryResult result)
void LoadSessionPermissionsCallback(PreparedQueryResult result)
MessageBuffer _packetBuffer
Definition: WorldSocket.h:168
void SendAuthResponseError(uint32 code)
QueryCallbackProcessor _queryProcessor
Definition: WorldSocket.h:174
WorldPacketCrypt _authCrypt
Definition: WorldSocket.h:155
WorldSocket(boost::asio::ip::tcp::socket &&socket)
Definition: WorldSocket.cpp:59
void Start() override
Definition: WorldSocket.cpp:78
std::size_t _sendBufferSize
Definition: WorldSocket.h:170
void SetWorldSession(WorldSession *session)
uint32 _OverSpeedPings
Definition: WorldSocket.h:160
TimePoint _LastPingTime
Definition: WorldSocket.h:159
void HandleAuthSession(std::shared_ptr< WorldPackets::Auth::AuthSession > authSession)
void HandleSendAuthSession()
WorldSession * _worldSession
Definition: WorldSocket.h:163
uint32 CompressPacket(uint8 *buffer, WorldPacket const &packet)
uint64 _key
Definition: WorldSocket.h:152
std::string _ipCountry
Definition: WorldSocket.h:175
z_stream * _compressionStream
Definition: WorldSocket.h:172
static uint8 const EncryptionKeySeed[16]
Definition: WorldSocket.h:94
void SendPacket(WorldPacket const &packet)
static uint8 const AuthCheckSeed[16]
Definition: WorldSocket.h:91
MPSCQueue< EncryptablePacket, &EncryptablePacket::SocketQueueLink > _bufferQueue
Definition: WorldSocket.h:169
void SendPacketAndLogOpcode(WorldPacket const &packet)
sends and logs network.opcode without accessing WorldSession
void HandleEnterEncryptedModeAck()
bool HandlePing(WorldPackets::Auth::Ping &ping)
static std::string const ServerConnectionInitialize
Definition: WorldSocket.h:87
std::array< uint8, 16 > _encryptKey
Definition: WorldSocket.h:157
void WritePacketToBuffer(EncryptablePacket const &packet, MessageBuffer &buffer)
void ReadHandler() override
std::array< uint8, 16 > _serverChallenge
Definition: WorldSocket.h:154
std::mutex _worldSessionLock
Definition: WorldSocket.h:162
void OnClose() override
ReadDataHandlerResult ReadDataHandler()
static uint32 const MinSizeForCompression
Definition: WorldSocket.h:89
void LoadFromDBCallback(PreparedQueryResult result)
Definition: RBAC.cpp:200
ConnectionType
Definition: Opcodes.h:29
OpcodeClient
Definition: Opcodes.h:46
std::string GetOpcodeNameForLogging(OpcodeClient opcode)
Lookup opcode name for human understandable logging.
Definition: Opcodes.cpp:2205
OpcodeTable opcodeTable
Definition: Opcodes.cpp:38
OpcodeServer
Definition: Opcodes.h:901
@ CONNECTION_TYPE_INSTANCE
Definition: Opcodes.h:31
@ CONNECTION_TYPE_REALM
Definition: Opcodes.h:30
@ CMSG_TIME_SYNC_RESPONSE
Definition: Opcodes.h:848
@ CMSG_CONNECT_TO_FAILED
Definition: Opcodes.h:282
@ CMSG_AUTH_CONTINUED_SESSION
Definition: Opcodes.h:93
@ CMSG_ENTER_ENCRYPTED_MODE_ACK
Definition: Opcodes.h:337
@ CMSG_ENABLE_NAGLE
Definition: Opcodes.h:334
@ CMSG_PING
Definition: Opcodes.h:633
@ CMSG_LOG_DISCONNECT
Definition: Opcodes.h:474
@ CMSG_HOTFIX_REQUEST
Definition: Opcodes.h:436
@ CMSG_KEEP_ALIVE
Definition: Opcodes.h:447
@ CMSG_AUTH_SESSION
Definition: Opcodes.h:94
@ SMSG_COMPRESSED_PACKET
Definition: Opcodes.h:2086
@ NUM_OPCODE_HANDLERS
Definition: Opcodes.h:40
#define sWorld
Definition: World.h:931
Realm realm
Definition: World.cpp:3966
@ CONFIG_MAX_OVERSPEED_PINGS
Definition: World.h:307
@ CONFIG_COMPRESSION
Definition: World.h:233
@ CONFIG_ALLOW_LOGGING_IP_ADDRESSES_IN_DATABASE
Definition: World.h:197
@ CONFIG_WARDEN_ENABLED
Definition: World.h:167
time_t GetGameTime()
Definition: GameTime.cpp:44
void TC_COMMON_API GetRandomBytes(uint8 *buf, size_t len)
@ RBAC_PERM_SKIP_CHECK_OVERSPEED_PING
Definition: RBAC.h:76
STL namespace.
std::string LockCountry
std::string OS
uint32 Recruiter
struct AccountInfo::@331 BattleNet
LocaleConstant Locale
bool IsBanned() const
AccountTypes Security
struct AccountInfo::@332 Game
AccountInfo(Field const *fields)
std::array< uint8, 64 > KeyData
std::string LastIP
Minutes TimezoneOffset
bool IsValidSize()
Definition: WorldSocket.h:75
uint32 Size
Definition: WorldSocket.h:72
uint8 Tag[12]
Definition: WorldSocket.h:73
std::array< uint8, 16 > Mac64AuthSeed
Definition: RealmList.h:39
std::array< uint8, 16 > Win64AuthSeed
Definition: RealmList.h:38
uint32 Build
Definition: Realm.h:83
Battlenet::RealmHandle Id
Definition: Realm.h:82
struct WorldSession::ConnectToKey::@330 Fields