Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
WorldSocket.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6#include "AccountMgr.h"
7#include "BigNumber.h"
8#include "ByteBuffer.h"
9#include "Common.h"
10#include "CryptoHash.h"
11#include "CryptoRandom.h"
12#include "DatabaseEnv.h"
13#include "Log.h"
16#include "Opcodes.h"
17#include "PacketLog.h"
18#include "Player.h"
19#include "Platform/TimeUtils.h"
20#include "ScriptMgr.h"
21#include "SharedDefines.h"
22#include "Util.h"
23#include "World.h"
24#include "WorldPacket.h"
25#include "WorldSession.h"
26#include "WorldSocket.h"
27#include "WorldSocketMgr.h"
28#include <boost/asio/buffer.hpp>
29#include <boost/asio/error.hpp>
30#include <algorithm>
31#include <cerrno>
32#include <chrono>
33#include <new>
34#include <utility>
35
36#if defined(__GNUC__)
37#pragma pack(1)
38#else
39#pragma pack(push, 1)
40#endif
41
43{
45 {
46 if (_authCrypt->IsInitialized())
47 {
48 uint32 data = (size << 13) | (cmd & MAX_OPCODE);
49 memcpy(&header[0], &data, 4);
50 _authCrypt->EncryptSend((uint8*)&header[0], getHeaderLength());
51 }
52 else
53 {
54 // Dynamic header size is not needed anymore, we are using not encrypted part for only the first few packets
55 memcpy(&header[0], &size, 2);
56 memcpy(&header[2], &cmd, 2);
57 }
58 }
59
61 {
62 return 4;
63 }
64
65 const uint32 size;
67};
68
74
80
81#if defined(__GNUC__)
82#pragma pack()
83#else
84#pragma pack(pop)
85#endif
86
87namespace
88{
89bool IsHushedUnhandledClientOpcode(uint16 opcode)
90{
91 switch (opcode)
92 {
93 case 0x18B2:
94 case 0x044E:
95 case 0x0CF0:
96 case 0x1124:
97 return true;
98 default:
99 return false;
100 }
101}
102}
103
104WorldSocket::WorldSocket(std::unique_ptr<WorldSocketHandle> socket, std::string remoteAddress) :
105m_LastPingTime(), m_HasLastPingTime(false), m_OverSpeedPings(0), m_Address(std::move(remoteAddress)),
108m_PendingOutput(), m_OutBufferSize(65536), m_Started(false), m_Socket(std::move(socket)),
109m_WriteQueue(new Skyfire::Net::BoostAsioWriteQueue<WorldSocketHandle>(*m_Socket)),
111{
113}
114
116{
117 sPacketLog->CloseSession(this);
118 delete m_RecvWPct;
119 CloseSocket();
120}
121
122bool WorldSocket::IsClosed(void) const
123{
124 return m_Closed;
125}
126
128{
129 if (m_Closed.exchange(true))
130 return;
131
132 {
133 GuardType Guard(m_SendLock);
134 m_PendingOutput.clear();
135 }
136
137 if (m_Socket && m_Socket->is_open())
139}
140
141const std::string& WorldSocket::GetRemoteAddress(void) const
142{
143 return m_Address;
144}
145
147{
148 GuardType Guard(m_SendLock);
149 return !m_PendingOutput.empty() || (m_WriteQueue && m_WriteQueue->HasPendingOutput());
150}
151
153{
154 return m_Socket && m_Socket->is_open();
155}
156
158{
159 GuardType Guard(m_SendLock);
160
161 if (m_Closed)
162 return -1;
163
164 // Dump outgoing packet
165 if (sPacketLog->CanLogPacket())
166 sPacketLog->LogPacket(this, BuildPacketLogSessionInfo(), pct, SERVER_TO_CLIENT);
167
168 WorldPacket const* pkt = &pct;
169
170 // Empty buffer used in case packet should be compressed
171 // Disable compression for now :)
172 /* WorldPacket buff;
173 if (m_Session && pkt->size() > 0x400)
174 {
175 buff.Compress(m_Session->GetCompressionStream(), pkt);
176 pkt = &buff;
177 }*/
178
179 uint16 opcodeNumber = serverOpcodeTable[pkt->GetOpcode()]->OpcodeNumber;
180
181 m_SessionState.WithSession([pkt](WorldSession* session)
182 {
183 if (session)
184 SF_LOG_TRACE("network.opcode", "S->C: %s %s", session->GetPlayerInfo().c_str(), GetOpcodeNameForLogging(pkt->GetOpcode(), true).c_str());
185 });
186
187 sScriptMgr->OnPacketSend(this, *pkt);
188
189 ServerPktHeader header(!m_Crypt.IsInitialized() ? pkt->size() + 2 : pct.size(), opcodeNumber, &m_Crypt);
190
191 size_t packetSize = pkt->size() + header.getHeaderLength();
192 std::vector<char> serialized;
193 serialized.reserve(packetSize);
194 serialized.insert(serialized.end(), reinterpret_cast<char*>(header.header),
195 reinterpret_cast<char*>(header.header) + header.getHeaderLength());
196 if (!pkt->empty())
197 serialized.insert(serialized.end(), reinterpret_cast<char const*>(pkt->contents()),
198 reinterpret_cast<char const*>(pkt->contents()) + pkt->size());
199
200 if (!m_Started)
201 m_PendingOutput.push_back(std::move(serialized));
202 else
203 QueueSerializedPacket(std::move(serialized));
204
205 return 0;
206}
207
208void WorldSocket::LogPacketMarker(std::string const& marker)
209{
210 if (sPacketLog->CanLogPacket())
211 sPacketLog->LogMarker(this, BuildPacketLogSessionInfo(), marker);
212}
213
215{
216 sPacketLog->RefreshSessionInfo(this, BuildPacketLogSessionInfo());
217}
218
220{
221 return ++m_ReferenceCount;
222}
223
225{
226 long result = --m_ReferenceCount;
227 if (result == 0)
228 delete this;
229
230 return result;
231}
232
234{
235 // not an opcode. this packet sends raw string WORLD OF WARCRAFT CONNECTION - SERVER TO CLIENT"
236 // because of our implementation, bytes "WO" become the opcode
238 packet << std::string("RLD OF WARCRAFT CONNECTION - SERVER TO CLIENT");
239
240 if (SendPacket(packet) == -1)
241 return -1;
242
243 return 0;
244}
245
246void WorldSocket::Start(std::function<void(WorldSocket*)> closeHandler)
247{
248 m_CloseHandler = std::move(closeHandler);
249
250 if (m_Closed)
251 {
252 NotifyClosed();
253 return;
254 }
255
256 {
257 GuardType Guard(m_SendLock);
258 m_Started = true;
259
260 while (!m_PendingOutput.empty())
261 {
262 QueueSerializedPacket(std::move(m_PendingOutput.front()));
263 m_PendingOutput.pop_front();
264 }
265 }
266
268}
269
271{
272 m_SessionState.Detach(session);
273}
274
275void WorldSocket::QueueSerializedPacket(std::vector<char> data)
276{
277 if (!IsValidSocket())
278 {
279 NotifyClosed();
280 return;
281 }
282
283 AddReference();
284 m_WriteQueue->Queue(std::move(data),
285 [this](boost::system::error_code const& error, size_t transferredBytes)
286 {
287 HandleAsyncWrite(error, transferredBytes);
289 });
290}
291
293{
294 if (m_Closed || !IsValidSocket())
295 {
296 NotifyClosed();
297 return;
298 }
299
300 AddReference();
301 m_Socket->async_read_some(boost::asio::buffer(m_ReadBuffer),
302 [this](boost::system::error_code const& error, size_t transferredBytes)
303 {
304 HandleAsyncRead(error, transferredBytes);
306 });
307}
308
309void WorldSocket::HandleAsyncRead(boost::system::error_code const& error, size_t transferredBytes)
310{
311 if (error)
312 {
313 if (error != boost::asio::error::operation_aborted && error != boost::asio::error::eof)
314 SF_LOG_DEBUG("network", "WorldSocket::HandleAsyncRead: peer error closing connection error = %d", error.value());
315
316 CloseSocket();
317 NotifyClosed();
318 return;
319 }
320
321 if (transferredBytes == 0)
322 {
323 SF_LOG_DEBUG("network", "WorldSocket::HandleAsyncRead: Peer has closed connection");
324 CloseSocket();
325 NotifyClosed();
326 return;
327 }
328
329 errno = 0;
330 int result = handle_input_missing_data(m_ReadBuffer.data(), transferredBytes);
331 if (result == -1 && errno != EWOULDBLOCK && errno != EAGAIN)
332 {
333 SF_LOG_DEBUG("network", "WorldSocket::HandleAsyncRead: packet processing error, errno = %d", errno);
334 CloseSocket();
335 NotifyClosed();
336 return;
337 }
338
339 if (m_Closed)
340 {
341 NotifyClosed();
342 return;
343 }
344
346}
347
348void WorldSocket::HandleAsyncWrite(boost::system::error_code const& error, size_t transferredBytes)
349{
350 (void)transferredBytes;
351
352 if (error)
353 {
354 if (error != boost::asio::error::operation_aborted)
355 SF_LOG_DEBUG("network", "WorldSocket::HandleAsyncWrite: peer error closing connection error = %d", error.value());
356
357 CloseSocket();
358 NotifyClosed();
359 return;
360 }
361}
362
364{
365 if (m_CloseNotified.exchange(true))
366 return;
367
368 if (m_CloseHandler)
369 m_CloseHandler(this);
370}
371
373{
376 info.AccountId = 0;
377 info.RealmId = 0;
378 info.CharacterName = "none";
379
380 m_SessionState.WithSession([&info](WorldSession* session)
381 {
382 if (!session)
383 return;
384
385 info.AccountId = session->GetAccountId();
386 info.RealmId = session->GetVirtualRealmID();
387
388 if (Player* player = session->GetPlayer())
389 info.CharacterName = player->GetName();
390 });
391
392 return info;
393}
394
396{
397 ASSERT(m_RecvWPct == NULL);
398
399
400 if (m_Crypt.IsInitialized())
401 {
403 uint8* uintHeader = m_WorldHeader.data();
404 m_Crypt.DecryptRecv(uintHeader, sizeof(WorldClientPktHeader));
405 WorldClientPktHeader& header = *(WorldClientPktHeader*)uintHeader;
406
407 uint32 value = *(uint32*)uintHeader;
408 header.cmd = value & 0x1FFF;
409 header.size = ((value & ~(uint32)0x1FFF) >> 13);
410
411 if (header.size > 10236)
412 {
413 m_SessionState.WithSession([&header](WorldSession* session)
414 {
415 Player* _player = session ? session->GetPlayer() : NULL;
416 SF_LOG_ERROR("network", "WorldSocket::handle_input_header(): client (account: %u, char [GUID: %u, name: %s]) sent malformed packet (size: %d, cmd: %d)",
417 session ? session->GetAccountId() : 0,
418 _player ? _player->GetGUIDLow() : 0,
419 _player ? _player->GetName().c_str() : "<none>",
420 header.size, header.cmd);
421 });
422
423 errno = EINVAL;
424 return -1;
425 }
426
427 uint16 opcodeNumber = PacketFilter::DropHighBytes(header.cmd);
428 m_RecvWPct = new (std::nothrow) WorldPacket(clientOpcodeTable.GetOpcodeByNumber(opcodeNumber), header.size);
429 if (!m_RecvWPct)
430 return -1;
431 m_RecvWPct->SetReceivedOpcode(opcodeNumber);
432
433 if (header.size > 0)
434 {
435 m_RecvWPct->resize(header.size);
436 m_RecvPctRead = 0;
437 }
438 else
439 ASSERT(m_RecvPctRead == 0);
440 }
441 else
442 {
444 uint8* uintHeader = m_Header.data();
445 AuthClientPktHeader& header = *((AuthClientPktHeader*)uintHeader);
446
447 if ((header.size < 4) || (header.size > 10240))
448 {
449 m_SessionState.WithSession([&header](WorldSession* session)
450 {
451 Player* _player = session ? session->GetPlayer() : NULL;
452 SF_LOG_ERROR("network", "WorldSocket::handle_input_header(): client (account: %u, char [GUID: %u, name: %s]) sent malformed packet (size: %d, cmd: %d)",
453 session ? session->GetAccountId() : 0,
454 _player ? _player->GetGUIDLow() : 0,
455 _player ? _player->GetName().c_str() : "<none>",
456 header.size, header.cmd);
457 });
458
459 errno = EINVAL;
460 return -1;
461 }
462
463 header.size -= 4;
464
465 uint16 opcodeNumber = PacketFilter::DropHighBytes(header.cmd);
466 m_RecvWPct = new (std::nothrow) WorldPacket(clientOpcodeTable.GetOpcodeByNumber(opcodeNumber), header.size);
467 if (!m_RecvWPct)
468 return -1;
469 m_RecvWPct->SetReceivedOpcode(opcodeNumber);
470
471 if (header.size > 0)
472 {
473 m_RecvWPct->resize(header.size);
474 m_RecvPctRead = 0;
475 }
476 else
477 ASSERT(m_RecvPctRead == 0);
478 }
479
480 return 0;
481}
483{
484 // set errno properly here on error !!!
485 // now have a header and payload
486
487 if (m_Crypt.IsInitialized())
488 {
489 ASSERT(m_RecvWPct == NULL || m_RecvPctRead == m_RecvWPct->size());
491 ASSERT(m_RecvWPct != NULL);
492
493 const int ret = ProcessIncoming(m_RecvWPct);
494
495 m_RecvPctRead = 0;
496 m_RecvWPct = NULL;
497
499
500 if (ret == -1)
501 errno = EINVAL;
502
503 return ret;
504 }
505 else
506 {
507 ASSERT(m_RecvWPct == NULL || m_RecvPctRead == m_RecvWPct->size());
509 ASSERT(m_RecvWPct != NULL);
510
511 const int ret = ProcessIncoming(m_RecvWPct);
512
513 m_RecvPctRead = 0;
514 m_RecvWPct = NULL;
515
516 m_HeaderRead = 0;
517
518 if (ret == -1)
519 errno = EINVAL;
520
521 return ret;
522 }
523}
524
525int WorldSocket::handle_input_missing_data(char const* data, size_t length)
526{
527 size_t readPos = 0;
528
529 while (readPos < length)
530 {
531 if (m_Crypt.IsInitialized())
532 {
533 if (m_WorldHeaderRead < m_WorldHeader.size())
534 {
535 //need to receive the header
536 const size_t needed = m_WorldHeader.size() - m_WorldHeaderRead;
537 const size_t available = length - readPos;
538 const size_t to_header = std::min(available, needed);
539 memcpy(m_WorldHeader.data() + m_WorldHeaderRead, data + readPos, to_header);
540 m_WorldHeaderRead += to_header;
541 readPos += to_header;
542
543 if (m_WorldHeaderRead < m_WorldHeader.size())
544 {
545 // Couldn't receive the whole header this time.
546 ASSERT(readPos == length);
547 errno = EWOULDBLOCK;
548 return -1;
549 }
550
551 // We just received nice new header
552 if (handle_input_header() == -1)
553 {
554 ASSERT((errno != EWOULDBLOCK) && (errno != EAGAIN));
555 return -1;
556 }
557 }
558 }
559 else
560 {
561 if (m_HeaderRead < m_Header.size())
562 {
563 //need to receive the header
564 const size_t needed = m_Header.size() - m_HeaderRead;
565 const size_t available = length - readPos;
566 const size_t to_header = std::min(available, needed);
567 memcpy(m_Header.data() + m_HeaderRead, data + readPos, to_header);
568 m_HeaderRead += to_header;
569 readPos += to_header;
570
571 if (m_HeaderRead < m_Header.size())
572 {
573 // Couldn't receive the whole header this time.
574 ASSERT(readPos == length);
575 errno = EWOULDBLOCK;
576 return -1;
577 }
578
579 // We just received nice new header
580 if (handle_input_header() == -1)
581 {
582 ASSERT((errno != EWOULDBLOCK) && (errno != EAGAIN));
583 return -1;
584 }
585 }
586 }
587
588 // Its possible on some error situations that this happens
589 // for example on closing when epoll receives more chunked data and stuff
590 // hope this is not hack, as proper m_RecvWPct is asserted around
591 if (!m_RecvWPct)
592 {
593 SF_LOG_ERROR("network", "Forcing close on input m_RecvWPct = NULL");
594 errno = EINVAL;
595 return -1;
596 }
597
598 // We have full read header, now check the data payload
600 {
601 //need more data in the payload
602 const size_t needed = m_RecvWPct->size() - m_RecvPctRead;
603 const size_t available = length - readPos;
604 const size_t to_data = std::min(available, needed);
605 memcpy(reinterpret_cast<char*>(m_RecvWPct->contents()) + m_RecvPctRead, data + readPos, to_data);
606 m_RecvPctRead += to_data;
607 readPos += to_data;
608
610 {
611 // Couldn't receive the whole data this time.
612 ASSERT(readPos == length);
613 errno = EWOULDBLOCK;
614 return -1;
615 }
616 }
617
618 //just received fresh new payload
619 if (handle_input_payload() == -1)
620 {
621 ASSERT((errno != EWOULDBLOCK) && (errno != EAGAIN));
622 return -1;
623 }
624 }
625
626 return length == 4096 ? 1 : 2;
627}
628
630{
631 ASSERT(new_pct);
632
633 // manage memory ;)
634 std::unique_ptr<WorldPacket> aptr(new_pct);
635
636 Opcodes opcode = new_pct->GetOpcode();
637
638 if (m_Closed)
639 return -1;
640
641 // Dump received packet.
642 if (sPacketLog->CanLogPacket())
643 sPacketLog->LogPacket(this, BuildPacketLogSessionInfo(), *new_pct, CLIENT_TO_SERVER);
644
645 std::string opcodeName = GetOpcodeNameForLogging(opcode, false);
646 m_SessionState.WithSession([&opcodeName](WorldSession* session)
647 {
648 if (session)
649 SF_LOG_TRACE("network.opcode", "C->S: %s %s", session->GetPlayerInfo().c_str(), opcodeName.c_str());
650 });
651
652 try
653 {
654 switch (opcode)
655 {
656 case CMSG_PING:
657 return HandlePing(*new_pct);
659 if (m_SessionState.HasSession())
660 {
661 m_SessionState.WithSession([](WorldSession* session)
662 {
663 SF_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s",
664 session ? session->GetPlayerInfo().c_str() : "<detached>");
665 });
666 return -1;
667 }
668
669 sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct));
670 return HandleAuthSession(*new_pct);
671 case CMSG_KEEP_ALIVE:
672 sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct));
673 return 0;
675 new_pct->rfinish(); // contains uint32 disconnectReason;
676 sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct));
677 return 0;
678 // not an opcode, client sends string "WORLD OF WARCRAFT CONNECTION - CLIENT TO SERVER" without opcode
679 // first 4 bytes become the opcode (2 dropped)
681 {
682 sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct));
683 std::string str;
684 *new_pct >> str;
685 if (str != "D OF WARCRAFT CONNECTION - CLIENT TO SERVER")
686 return -1;
687 return HandleSendAuthSession();
688 }
690 {
691 SF_LOG_DEBUG("network", "%s", opcodeName.c_str());
692 sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct));
693 return m_SessionState.WithSession([](WorldSession* session)
694 {
695 return session ? session->HandleEnableNagleAlgorithm() : 0;
696 });
697 }
698 default:
699 {
700 int result = 0;
701 m_SessionState.WithSession([&result, &aptr, new_pct, opcode](WorldSession* session)
702 {
703 if (!session)
704 {
705 SF_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode));
706 result = -1;
707 return;
708 }
709
710 // prevent invalid memory access/crash with custom opcodes
711 if (opcode >= NUM_OPCODES)
712 {
713 result = 0;
714 return;
715 }
716
717 OpcodeHandler const* handler = clientOpcodeTable[opcode];
718 if (!handler || handler->Status == STATUS_UNHANDLED)
719 {
720 if (!IsHushedUnhandledClientOpcode(new_pct->GetReceivedOpcode()))
721 SF_LOG_ERROR("network.opcode", "No defined handler for opcode %s sent by %s", GetOpcodeNameForLogging(new_pct->GetOpcode(), false, new_pct->GetReceivedOpcode()).c_str(), session->GetPlayerInfo().c_str());
722
723 result = 0;
724 return;
725 }
726
727 // Our Idle timer will reset on any non PING opcodes.
728 // Catches people idling on the login screen and any lingering ingame connections.
729 session->ResetTimeOutTime();
730
731 // OK, give the packet to WorldSession
732 aptr.release();
733 session->QueuePacket(new_pct);
734 result = 0;
735 });
736 return result;
737 }
738 }
739 }
740 catch (ByteBufferException&)
741 {
742 int32 accountId = m_SessionState.WithSession([](WorldSession* session)
743 {
744 return session ? int32(session->GetAccountId()) : -1;
745 });
746
747 SF_LOG_ERROR("network", "WorldSocket::ProcessIncoming ByteBufferException occured while parsing an instant handled packet %s from client %s, accountid=%i. Disconnected client.",
748 opcodeName.c_str(), GetRemoteAddress().c_str(), accountId);
749 new_pct->hexlike();
750 return -1;
751 }
752
753 return 0;
754}
755
757{
759 packet << uint16(0);
760
761 packet.append(SkyFire::Crypto::GetRandomBytes<32>()); // new encryption seeds
762
763 packet << uint8(1);
764 packet.append(m_Seed);
765
766 return SendPacket(packet);
767}
768
770{
771 uint8 security;
772 uint16 clientBuild;
773 uint32 id;
774 uint32 addonSize;
775 LocaleConstant locale;
776 std::string account;
777 WorldPacket addonsData;
778 std::array<uint8, 4> clientSeed;
780 uint32 VirtualRealmID;
781
782 recvPacket.read_skip<uint32>();
783 recvPacket.read_skip<uint32>();
784 recvPacket >> digest[18];
785 recvPacket >> digest[14];
786 recvPacket >> digest[3];
787 recvPacket >> digest[4];
788 recvPacket >> digest[0];
789 recvPacket >> VirtualRealmID;
790 recvPacket >> digest[11];
791 recvPacket.read(clientSeed);
792 recvPacket >> digest[19];
793 recvPacket.read_skip<uint8>();
794 recvPacket.read_skip<uint8>();
795 recvPacket >> digest[2];
796 recvPacket >> digest[9];
797 recvPacket >> digest[12];
798 recvPacket.read_skip<uint64>();
799 recvPacket.read_skip<uint32>();
800 recvPacket >> digest[16];
801 recvPacket >> digest[5];
802 recvPacket >> digest[6];
803 recvPacket >> digest[8];
804 recvPacket >> clientBuild;
805 recvPacket >> digest[17];
806 recvPacket >> digest[7];
807 recvPacket >> digest[13];
808 recvPacket >> digest[15];
809 recvPacket >> digest[1];
810 recvPacket >> digest[10];
811 recvPacket >> addonSize;
812
813 addonsData.resize(addonSize);
814 recvPacket.read((uint8*)addonsData.contents(), addonSize);
815
816 recvPacket.ReadBit();
817 uint32 accountNameLength = recvPacket.ReadBits(11);
818
819 account = recvPacket.ReadString(accountNameLength);
820
821 if (sWorld->IsClosed())
822 {
824 SF_LOG_ERROR("network", "WorldSocket::HandleAuthSession: World closed, denying client (%s).", GetRemoteAddress().c_str());
825 return -1;
826 }
827
828 // Get the account information from the realmd database
829 // 0 1 2 3 4 5 6 7 8
830 // SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ?
832
833 stmt->setString(0, account);
834
835 PreparedQueryResult result = LoginDatabase.Query(stmt);
836
837 // Stop if the account is not found
838 if (!result)
839 {
841 SF_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account).");
842 return -1;
843 }
844
845 Field* fields = result->Fetch();
846
847 uint8 expansion = fields[4].GetUInt8();
848 uint32 world_expansion = sWorld->getIntConfig(WorldIntConfigs::CONFIG_EXPANSION);
849 if (expansion > world_expansion)
850 expansion = world_expansion;
851
853 if (fields[3].GetUInt8() == 1) // if ip is locked
854 {
855 if (strcmp(fields[2].GetCString(), GetRemoteAddress().c_str()))
856 {
858 SF_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs).");
859 return -1;
860 }
861 }
862
863 id = fields[0].GetUInt32();
864
865 SessionKey sessionKey = fields[1].GetBinary<SESSION_KEY_LENGTH>();
866
867 int64 mutetime = fields[5].GetInt64();
869 if (mutetime < 0)
870 {
871 mutetime = time(NULL) + llabs(mutetime);
872
873 stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME_LOGIN);
874
875 stmt->setInt64(0, mutetime);
876 stmt->setUInt32(1, id);
877
878 LoginDatabase.Execute(stmt);
879 }
880
881 locale = LocaleConstant(fields[6].GetUInt8());
882 if (locale >= TOTAL_LOCALES)
883 locale = LOCALE_enUS;
884
885 uint32 recruiter = fields[7].GetUInt32();
886 std::string os = fields[8].GetString();
887 bool hasBoost = fields[9].GetBool();
888
889 // Must be done before WorldSession is created
890 if (sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_WARDEN_ENABLED) && os != "Win" && os != "OSX")
891 {
893 SF_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", GetRemoteAddress().c_str(), os.c_str());
894 return -1;
895 }
896
897 // Checks gmlevel per Realm
898 stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID);
899
900 stmt->setUInt32(0, id);
901 stmt->setInt32(1, int32(VirtualRealmID));
902
903 result = LoginDatabase.Query(stmt);
904
905 if (!result)
906 security = 0;
907 else
908 {
909 fields = result->Fetch();
910 security = fields[0].GetUInt8();
911 }
912
913 // Re-check account ban (same check as in realmd)
914 stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BANS);
915
916 stmt->setUInt32(0, id);
917 stmt->setString(1, GetRemoteAddress());
918
919 PreparedQueryResult banresult = LoginDatabase.Query(stmt);
920
921 if (banresult) // if account banned
922 {
924 SF_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned).");
925 return -1;
926 }
927
928 // Check locked state for server
929 AccountTypes allowedAccountType = sWorld->GetPlayerSecurityLimit();
930 SF_LOG_DEBUG("network", "Allowed Level: %u Player Level %u", uint8(allowedAccountType), security);
931 if (allowedAccountType > AccountTypes::SEC_PLAYER && AccountTypes(security) < allowedAccountType)
932 {
934 SF_LOG_INFO("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough");
935 return -1;
936 }
937
938 // Check that Key and account name are the same on client and server
939 uint8 t[4] = { 0x00, 0x00, 0x00, 0x00 };
940
942 sha.UpdateData(account);
943 sha.UpdateData(t);
944 sha.UpdateData(clientSeed);
945 sha.UpdateData(m_Seed);
946 sha.UpdateData(sessionKey);
947 sha.Finalize();
948
949 std::string address = GetRemoteAddress();
950
951 if (sha.GetDigest() != digest)
952 {
954 SF_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", id, account.c_str(), address.c_str());
955 return -1;
956 }
957
958 SF_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.",
959 account.c_str(),
960 address.c_str());
961
962 // Check if this user is by any chance a recruiter
963 stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_RECRUITER);
964
965 stmt->setUInt32(0, id);
966
967 result = LoginDatabase.Query(stmt);
968
969 bool isRecruiter = false;
970 if (result)
971 isRecruiter = true;
972
973 // Update the last_ip in the database
974
975 stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_IP);
976
977 stmt->setString(0, address);
978 stmt->setString(1, account);
979
980 LoginDatabase.Execute(stmt);
981
982 // NOTE ATM the socket is single-threaded, have this in mind ...
983 WorldSession* session = new (std::nothrow) WorldSession(id, this, AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter, hasBoost);
984 if (!session)
985 return -1;
986
987 if (!m_SessionState.Attach(session))
988 {
989 delete session;
990 return -1;
991 }
992
993 m_Crypt.Init(sessionKey);
994
995 session->LoadGlobalAccountData();
996 session->LoadTutorialsData();
997 session->ReadAddonsInfo(addonsData);
998 session->LoadPermissions();
999
1000 session->SetVirtualRealmID(VirtualRealmID);
1001
1002 // Initialize Warden system only if it is enabled by config
1004 session->InitWarden(sessionKey, os);
1005
1006 // Sleep this Network thread for
1007 uint32 sleepTime = sWorld->getIntConfig(WorldIntConfigs::CONFIG_SESSION_ADD_DELAY);
1009
1010 sWorld->AddSession(session);
1011 return 0;
1012}
1013
1015{
1016 uint32 ping;
1017 uint32 latency;
1018
1019 // Get the ping packet content
1020 recvPacket >> latency;
1021 recvPacket >> ping;
1022
1023 if (!m_HasLastPingTime)
1024 {
1025 m_LastPingTime = std::chrono::steady_clock::now(); // for 1st ping
1026 m_HasLastPingTime = true;
1027 }
1028 else
1029 {
1030 std::chrono::steady_clock::time_point cur_time = std::chrono::steady_clock::now();
1031 auto diff_time = cur_time - m_LastPingTime;
1032 m_LastPingTime = cur_time;
1033
1034 if (diff_time < std::chrono::seconds(27))
1035 {
1037
1039
1040 if (max_count && m_OverSpeedPings > max_count)
1041 {
1042 bool kickForOverspeed = m_SessionState.WithSession([this](WorldSession* session)
1043 {
1045 return false;
1046
1047 SF_LOG_ERROR("network", "WorldSocket::HandlePing: %s kicked for over-speed pings (address: %s)",
1048 session->GetPlayerInfo().c_str(), GetRemoteAddress().c_str());
1049
1050 return true;
1051 });
1052
1053 if (kickForOverspeed)
1054 return -1;
1055 }
1056 }
1057 else
1058 m_OverSpeedPings = 0;
1059 }
1060
1061 // critical section
1062 bool updatedSession = m_SessionState.WithSession([this, latency](WorldSession* session)
1063 {
1064 if (session)
1065 {
1066 session->SetLatency(latency);
1067 session->ResetClientTimeDelay();
1068 return true;
1069 }
1070
1071 SF_LOG_ERROR("network", "WorldSocket::HandlePing: peer sent CMSG_PING, "
1072 "but is not authenticated or got recently kicked, "
1073 " address = %s",
1074 GetRemoteAddress().c_str());
1075 return false;
1076 });
1077
1078 if (!updatedSession)
1079 {
1080 return -1;
1081 }
1082
1083 WorldPacket packet(SMSG_PONG, 4);
1084 packet << ping;
1085 return SendPacket(packet);
1086}
1087
1089{
1091 packet.WriteBit(0); // has account info
1092 packet.WriteBit(0); // has queue info
1093 packet << uint8(code);
1094 SendPacket(packet);
1095}
constexpr size_t SESSION_KEY_LENGTH
Definition AuthDefines.h:7
std::array< uint8, SESSION_KEY_LENGTH > SessionKey
Definition AuthDefines.h:8
const uint8 TOTAL_LOCALES
Definition Common.h:153
LocaleConstant
Definition Common.h:138
@ LOCALE_enUS
Definition Common.h:139
#define llabs
Definition Common.h:102
AccountTypes
Definition Common.h:129
std::int32_t int32
Definition Define.h:73
std::uint8_t uint8
Definition Define.h:79
std::uint32_t uint32
Definition Define.h:77
std::uint64_t uint64
Definition Define.h:76
std::int64_t int64
Definition Define.h:72
std::uint16_t uint16
Definition Define.h:78
#define ASSERT
Definition Errors.h:29
#define SF_LOG_DEBUG(filterType__,...)
Definition Log.h:134
#define SF_LOG_ERROR(filterType__,...)
Definition Log.h:143
#define SF_LOG_TRACE(filterType__,...)
Definition Log.h:131
#define SF_LOG_INFO(filterType__,...)
Definition Log.h:137
@ LOGIN_UPD_MUTE_TIME_LOGIN
@ LOGIN_SEL_BANS
@ LOGIN_GET_GMLEVEL_BY_REALMID
@ LOGIN_UPD_LAST_IP
@ LOGIN_SEL_ACCOUNT_RECRUITER
@ LOGIN_SEL_ACCOUNT_INFO_BY_NAME
#define sPacketLog
Definition PacketLog.h:47
@ SERVER_TO_CLIENT
Definition PacketLog.h:16
@ CLIENT_TO_SERVER
Definition PacketLog.h:15
Skyfire::AutoPtr< PreparedResultSet, Skyfire::Mutex > PreparedQueryResult
Definition QueryResult.h:94
#define sScriptMgr
Definition ScriptMgr.h:764
ResponseCodes
bool IsInitialized() const
Definition AuthCrypt.h:22
bool WriteBit(uint32 bit)
Definition ByteBuffer.h:164
void read_skip()
Definition ByteBuffer.h:502
uint32 ReadBits(size_t bits)
Definition ByteBuffer.h:198
void resize(size_t newsize)
Definition ByteBuffer.h:612
void hexlike() const
void append(T value)
Definition ByteBuffer.h:147
void rfinish()
Definition ByteBuffer.h:478
std::string ReadString(size_t length)
Definition ByteBuffer.h:565
size_t size() const
Definition ByteBuffer.h:609
bool empty() const
Definition ByteBuffer.h:610
uint8 * contents()
Definition ByteBuffer.h:605
bool ReadBit()
Definition ByteBuffer.h:180
Definition Field.h:16
uint8 GetUInt8() const
Definition Field.h:26
std::string GetString() const
Definition Field.h:228
std::vector< uint8 > GetBinary() const
Definition Field.cpp:56
int64 GetInt64() const
Definition Field.h:159
bool GetBool() const
Definition Field.h:21
uint32 GetUInt32() const
Definition Field.h:105
uint32 GetGUIDLow() const
Definition Object.h:120
static uint16 DropHighBytes(uint16 opcode)
void setString(const uint8 index, const std::string &value)
void setInt64(const uint8 index, const int64 value)
void setUInt32(const uint8 index, const uint32 value)
void setInt32(const uint8 index, const int32 value)
Digest const & GetDigest() const
Definition CryptoHash.h:93
void UpdateData(uint8 const *data, size_t len)
Definition CryptoHash.h:72
std::string const & GetName() const
Definition Object.h:664
uint16 GetReceivedOpcode()
Definition WorldPacket.h:43
Opcodes GetOpcode() const
Definition WorldPacket.h:38
Player session in the World.
void ReadAddonsInfo(WorldPacket &data)
void QueuePacket(WorldPacket *new_packet)
Add an incoming packet to the queue.
std::string GetPlayerInfo() const
Player * GetPlayer() const
void SetVirtualRealmID(uint32 VRealmID)
void LoadTutorialsData()
uint32 GetVirtualRealmID() const
bool HasPermission(uint32 permissionId)
uint32 GetAccountId() const
int32 HandleEnableNagleAlgorithm()
void ResetTimeOutTime()
void InitWarden(SessionKey const &, std::string const &os)
void SetLatency(uint32 latency)
void LoadGlobalAccountData()
void ResetClientTimeDelay()
size_t m_OutBufferSize
Configured output buffer size retained for network option compatibility.
bool IsClosed(void) const
Check if socket is closed.
size_t m_WorldHeaderRead
void StartAsyncRead()
WorldSocket(std::unique_ptr< WorldSocketHandle > socket, std::string remoteAddress)
void HandleAsyncWrite(boost::system::error_code const &error, size_t transferredBytes)
long RemoveReference(void)
Remove reference to this object.
std::unique_ptr< WorldSocketHandle > m_Socket
std::atomic< bool > m_CloseNotified
std::vector< uint8 > m_WorldHeader
void SendAuthResponseError(ResponseCodes code)
WorldPacket * m_RecvWPct
here are stored the fragments of the received data
std::atomic< long > m_ReferenceCount
WorldSocketSessionState m_SessionState
Session to which received packets are routed.
long AddReference(void)
Add reference to this object.
std::chrono::steady_clock::time_point m_LastPingTime
Time in which the last ping was received.
int ProcessIncoming(WorldPacket *new_pct)
size_t m_RecvPctRead
AuthCrypt m_Crypt
Class used for managing encryption of the headers.
std::unique_ptr< Skyfire::Net::BoostAsioWriteQueue< WorldSocketHandle > > m_WriteQueue
int HandleSendAuthSession()
Called by MSG_VERIFY_CONNECTIVITY_RESPONSE.
uint32 m_OverSpeedPings
Keep track of over-speed pings, to prevent ping flood.
LockType m_SendLock
Mutex for protecting packet serialization and pre-start output.
void RefreshPacketLogSessionInfo()
Refreshes packet log metadata after a session gains character context.
int handle_input_header(void)
Helper functions for processing incoming data.
bool HasPendingOutput(void) const
Returns true when outgoing data is waiting to be flushed.
void Start(std::function< void(WorldSocket *)> closeHandler)
Starts asynchronous socket processing.
int HandleAuthSession(WorldPacket &recvPacket)
Called by ProcessIncoming() on CMSG_AUTH_SESSION.
void CloseSocket(void)
Close the socket.
void HandleAsyncRead(boost::system::error_code const &error, size_t transferredBytes)
void DetachSession(WorldSession *session)
Detaches the owning world session when the session is being removed.
int handle_input_payload(void)
std::atomic< bool > m_Closed
std::vector< uint8 > m_Header
std::unique_lock< LockType > GuardType
Definition WorldSocket.h:61
int Initialize(void)
Called after socket accept and manager setup.
const std::string & GetRemoteAddress(void) const
Get address of connected peer.
int HandlePing(WorldPacket &recvPacket)
Called by ProcessIncoming() on CMSG_PING.
Skyfire::PacketLogServerSessionInfo BuildPacketLogSessionInfo() const
bool IsValidSocket(void) const
void LogPacketMarker(std::string const &marker)
Writes a readable marker into the per-session packet log.
size_t m_HeaderRead
std::function< void(WorldSocket *)> m_CloseHandler
std::array< uint8, 4 > m_Seed
std::deque< std::vector< char > > m_PendingOutput
Queue used before the socket manager starts asynchronous processing.
std::array< char, 4096 > m_ReadBuffer
void QueueSerializedPacket(std::vector< char > data)
bool m_HasLastPingTime
std::string m_Address
Address of the remote peer.
int SendPacket(const WorldPacket &pct)
int handle_input_missing_data(char const *data, size_t length)
void NotifyClosed()
std::string GetOpcodeNameForLogging(Opcodes id, bool isServerOpcode, uint16 opcodeNumber=0)
Lookup opcode name for human understandable logging.
Definition Opcodes.h:1157
OpcodeTable clientOpcodeTable
Definition Opcodes.cpp:10
boost::asio::ip::tcp::socket WorldSocketHandle
Definition WorldSocket.h:31
OpcodeTable serverOpcodeTable
Definition Opcodes.cpp:9
Opcodes
List of Opcodes.
Definition Opcodes.h:21
@ STATUS_UNHANDLED
Definition Opcodes.h:1084
@ MSG_VERIFY_CONNECTIVITY
Definition Opcodes.h:502
@ NUM_OPCODES
Definition Opcodes.h:1072
@ CMSG_ENABLE_NAGLE
Definition Opcodes.h:151
@ CMSG_PING
Definition Opcodes.h:347
@ CMSG_LOG_DISCONNECT
Definition Opcodes.h:256
@ SMSG_PONG
Definition Opcodes.h:886
@ SMSG_AUTH_CHALLENGE
Definition Opcodes.h:533
@ CMSG_KEEP_ALIVE
Definition Opcodes.h:234
@ SMSG_AUTH_RESPONSE
Definition Opcodes.h:534
@ CMSG_AUTH_SESSION
Definition Opcodes.h:46
#define sWorld
Definition World.h:910
@ CONFIG_MAX_OVERSPEED_PINGS
Definition World.h:265
@ CONFIG_SESSION_ADD_DELAY
Definition World.h:207
@ CONFIG_WARDEN_ENABLED
Definition World.h:158
void GetRandomBytes(uint8 *buf, size_t len)
SkyFire::Impl::GenericHash< EVP_sha1, Constants::SHA1_DIGEST_LENGTH_BYTES > SHA1
Definition CryptoHash.h:103
std::array< uint8, S > GetRandomBytes()
void CloseTcpSocket(boost::asio::ip::tcp::socket &socket)
void SleepForMicroseconds(uint32 microseconds)
Definition TimeUtils.h:37
@ RBAC_PERM_SKIP_CHECK_OVERSPEED_PING
Definition RBAC.h:63
LoginDatabaseWorkerPool LoginDatabase
Definition Main.cpp:54
SessionStatus Status
Definition Opcodes.h:1106
ServerPktHeader(uint32 size, uint32 cmd, AuthCrypt *_authCrypt)
const uint32 size
uint8 getHeaderLength()