diff options
author | leak <leak@bitmx.net> | 2014-07-06 01:26:29 +0200 |
---|---|---|
committer | leak <leak@bitmx.net> | 2014-07-06 01:26:29 +0200 |
commit | 7befb26625dd1eeb237e223296d9ed8817297025 (patch) | |
tree | 3226885cc3d93e656bcd4e02ad4366bc77de5994 /src | |
parent | 021e18d152b68b9d2a0c5887bab7eb7b61ecd2ca (diff) |
Some groundwork for replacing the ACE based WorldSocket handling
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/Scripting/ScriptMgr.cpp | 10 | ||||
-rw-r--r-- | src/server/game/Scripting/ScriptMgr.h | 24 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/ServerPktHeader.h | 59 | ||||
-rw-r--r-- | src/server/game/Server/WorldSession.cpp | 20 | ||||
-rw-r--r-- | src/server/game/Server/WorldSession.h | 6 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocket.cpp | 1050 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocket.h | 218 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocketAcceptor.h | 67 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocketMgr.cpp | 360 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocketMgr.h | 78 | ||||
-rw-r--r-- | src/server/game/Server/WorldTcpSession.cpp | 407 | ||||
-rw-r--r-- | src/server/game/Server/WorldTcpSession.h | 81 | ||||
-rw-r--r-- | src/server/worldserver/Main.cpp | 18 |
13 files changed, 579 insertions, 1819 deletions
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 5821ae3eb3c..06f6094c511 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -400,35 +400,35 @@ void ScriptMgr::OnNetworkStop() FOREACH_SCRIPT(ServerScript)->OnNetworkStop(); } -void ScriptMgr::OnSocketOpen(WorldSocket* socket) +void ScriptMgr::OnSocketOpen(WorldTcpSession* socket) { ASSERT(socket); FOREACH_SCRIPT(ServerScript)->OnSocketOpen(socket); } -void ScriptMgr::OnSocketClose(WorldSocket* socket, bool wasNew) +void ScriptMgr::OnSocketClose(WorldTcpSession* socket, bool wasNew) { ASSERT(socket); FOREACH_SCRIPT(ServerScript)->OnSocketClose(socket, wasNew); } -void ScriptMgr::OnPacketReceive(WorldSocket* socket, WorldPacket packet) +void ScriptMgr::OnPacketReceive(WorldTcpSession* socket, WorldPacket packet) { ASSERT(socket); FOREACH_SCRIPT(ServerScript)->OnPacketReceive(socket, packet); } -void ScriptMgr::OnPacketSend(WorldSocket* socket, WorldPacket packet) +void ScriptMgr::OnPacketSend(WorldTcpSession* socket, WorldPacket packet) { ASSERT(socket); FOREACH_SCRIPT(ServerScript)->OnPacketSend(socket, packet); } -void ScriptMgr::OnUnknownPacketReceive(WorldSocket* socket, WorldPacket packet) +void ScriptMgr::OnUnknownPacketReceive(WorldTcpSession* socket, WorldPacket packet) { ASSERT(socket); diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 0126c649019..530d99b9ad2 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -57,7 +57,7 @@ class Transport; class Unit; class Vehicle; class WorldPacket; -class WorldSocket; +class WorldTcpSession; class WorldObject; struct AchievementCriteriaData; @@ -214,30 +214,30 @@ class ServerScript : public ScriptObject public: - // Called when reactive socket I/O is started (WorldSocketMgr). + // Called when reactive socket I/O is started (WorldTcpSessionMgr). virtual void OnNetworkStart() { } // Called when reactive I/O is stopped. virtual void OnNetworkStop() { } // Called when a remote socket establishes a connection to the server. Do not store the socket object. - virtual void OnSocketOpen(WorldSocket* /*socket*/) { } + virtual void OnSocketOpen(WorldTcpSession* /*socket*/) { } // Called when a socket is closed. Do not store the socket object, and do not rely on the connection // being open; it is not. - virtual void OnSocketClose(WorldSocket* /*socket*/, bool /*wasNew*/) { } + virtual void OnSocketClose(WorldTcpSession* /*socket*/, bool /*wasNew*/) { } // Called when a packet is sent to a client. The packet object is a copy of the original packet, so reading // and modifying it is safe. - virtual void OnPacketSend(WorldSocket* /*socket*/, WorldPacket& /*packet*/) { } + virtual void OnPacketSend(WorldTcpSession* /*socket*/, WorldPacket& /*packet*/) { } // Called when a (valid) packet is received by a client. The packet object is a copy of the original packet, so // reading and modifying it is safe. - virtual void OnPacketReceive(WorldSocket* /*socket*/, WorldPacket& /*packet*/) { } + virtual void OnPacketReceive(WorldTcpSession* /*socket*/, WorldPacket& /*packet*/) { } // Called when an invalid (unknown opcode) packet is received by a client. The packet is a reference to the orignal // packet; not a copy. This allows you to actually handle unknown packets (for whatever purpose). - virtual void OnUnknownPacketReceive(WorldSocket* /*socket*/, WorldPacket& /*packet*/) { } + virtual void OnUnknownPacketReceive(WorldTcpSession* /*socket*/, WorldPacket& /*packet*/) { } }; class WorldScript : public ScriptObject @@ -908,11 +908,11 @@ class ScriptMgr void OnNetworkStart(); void OnNetworkStop(); - void OnSocketOpen(WorldSocket* socket); - void OnSocketClose(WorldSocket* socket, bool wasNew); - void OnPacketReceive(WorldSocket* socket, WorldPacket packet); - void OnPacketSend(WorldSocket* socket, WorldPacket packet); - void OnUnknownPacketReceive(WorldSocket* socket, WorldPacket packet); + void OnSocketOpen(WorldTcpSession* socket); + void OnSocketClose(WorldTcpSession* socket, bool wasNew); + void OnPacketReceive(WorldTcpSession* socket, WorldPacket packet); + void OnPacketSend(WorldTcpSession* socket, WorldPacket packet); + void OnUnknownPacketReceive(WorldTcpSession* socket, WorldPacket packet); public: /* WorldScript */ diff --git a/src/server/game/Server/Protocol/ServerPktHeader.h b/src/server/game/Server/Protocol/ServerPktHeader.h new file mode 100644 index 00000000000..4b0dae9f0f3 --- /dev/null +++ b/src/server/game/Server/Protocol/ServerPktHeader.h @@ -0,0 +1,59 @@ +/* +* Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> +* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __SERVERPKTHDR_H__ +#define __SERVERPKTHDR_H__ + +#include "Log.h" + +struct ServerPktHeader +{ + /** + * size is the length of the payload _plus_ the length of the opcode + */ + ServerPktHeader(uint32 size, uint16 cmd) : size(size) + { + uint8 headerIndex=0; + if (isLargePacket()) + { + TC_LOG_DEBUG("network", "initializing large server to client packet. Size: %u, cmd: %u", size, cmd); + header[headerIndex++] = 0x80 | (0xFF & (size >> 16)); + } + header[headerIndex++] = 0xFF &(size >> 8); + header[headerIndex++] = 0xFF & size; + + header[headerIndex++] = 0xFF & cmd; + header[headerIndex++] = 0xFF & (cmd >> 8); + } + + uint8 getHeaderLength() + { + // cmd = 2 bytes, size= 2||3bytes + return 2 + (isLargePacket() ? 3 : 2); + } + + bool isLargePacket() const + { + return size > 0x7FFF; + } + + const uint32 size; + uint8 header[5]; +}; + +#endif diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 444c06e41b8..4f3112403db 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -20,7 +20,7 @@ \ingroup u2w */ -#include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it +#include "WorldTcpSession.h" #include "Config.h" #include "Common.h" #include "DatabaseEnv.h" @@ -97,7 +97,7 @@ bool WorldSessionFilter::Process(WorldPacket* packet) } /// WorldSession constructor -WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter): +WorldSession::WorldSession(uint32 id, WorldTcpSession* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter): m_muteTime(mute_time), m_timeOutTime(0), AntiDOS(this), @@ -130,8 +130,7 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 if (sock) { - m_Address = sock->GetRemoteAddress(); - sock->AddReference(); + m_Address = sock->GetRemoteIpAddress(); ResetTimeOutTime(); LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId()); // One-time query } @@ -150,7 +149,6 @@ WorldSession::~WorldSession() if (m_Socket) { m_Socket->CloseSocket(); - m_Socket->RemoveReference(); m_Socket = NULL; } @@ -227,8 +225,7 @@ void WorldSession::SendPacket(WorldPacket const* packet) } #endif // !TRINITY_DEBUG - if (m_Socket->SendPacket(*packet) == -1) - m_Socket->CloseSocket(); + m_Socket->AsyncWrite(*packet); } /// Add an incoming packet to the queue @@ -281,9 +278,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) uint32 processedPackets = 0; time_t currentTime = time(NULL); - while (m_Socket && !m_Socket->IsClosed() && - !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && - _recvQueue.next(packet, updater)) + while (m_Socket && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater)) { if (!AntiDOS.EvaluateOpcode(*packet, currentTime)) { @@ -402,7 +397,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) break; } - if (m_Socket && !m_Socket->IsClosed() && _warden) + if (m_Socket && m_Socket->IsOpen() && _warden) _warden->Update(); ProcessQueryCallbacks(); @@ -420,12 +415,11 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) _warden->Update(); ///- Cleanup socket pointer if need - if (m_Socket && m_Socket->IsClosed()) + if (m_Socket && !m_Socket->IsOpen()) { expireTime -= expireTime > diff ? diff : expireTime; if (expireTime < diff || forceExit) { - m_Socket->RemoveReference(); m_Socket = NULL; } } diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 4cb6ec2d7af..709650a8119 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -46,7 +46,7 @@ class SpellCastTargets; class Unit; class Warden; class WorldPacket; -class WorldSocket; +class WorldTcpSession; struct AreaTableEntry; struct AuctionEntry; struct DeclinedName; @@ -208,7 +208,7 @@ struct PacketCounter class WorldSession { public: - WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter); + WorldSession(uint32 id, WorldTcpSession* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter); ~WorldSession(); bool PlayerLoading() const { return m_playerLoading; } @@ -981,7 +981,7 @@ class WorldSession uint32 m_GUIDLow; // set logined or recently logout player (while m_playerRecentlyLogout set) Player* _player; - WorldSocket* m_Socket; + WorldTcpSession* m_Socket; std::string m_Address; // Current Remote Address // std::string m_LAddress; // Last Attempted Remote Adress - we can not set attempted ip for a non-existing session! diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp deleted file mode 100644 index d35ee80099d..00000000000 --- a/src/server/game/Server/WorldSocket.cpp +++ /dev/null @@ -1,1050 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <ace/Message_Block.h> -#include <ace/OS_NS_string.h> -#include <ace/OS_NS_unistd.h> -#include <ace/os_include/arpa/os_inet.h> -#include <ace/os_include/netinet/os_tcp.h> -#include <ace/os_include/sys/os_types.h> -#include <ace/os_include/sys/os_socket.h> -#include <ace/OS_NS_string.h> -#include <ace/Reactor.h> -#include <ace/Auto_Ptr.h> - -#include "WorldSocket.h" -#include "Common.h" -#include "Player.h" -#include "Util.h" -#include "World.h" -#include "WorldPacket.h" -#include "SharedDefines.h" -#include "ByteBuffer.h" -#include "Opcodes.h" -#include "DatabaseEnv.h" -#include "BigNumber.h" -#include "SHA1.h" -#include "WorldSession.h" -#include "WorldSocketMgr.h" -#include "Log.h" -#include "PacketLog.h" -#include "ScriptMgr.h" -#include "AccountMgr.h" - -#if defined(__GNUC__) -#pragma pack(1) -#else -#pragma pack(push, 1) -#endif - -struct ServerPktHeader -{ - /** - * size is the length of the payload _plus_ the length of the opcode - */ - ServerPktHeader(uint32 size, uint16 cmd) : size(size) - { - uint8 headerIndex=0; - if (isLargePacket()) - { - TC_LOG_DEBUG("network", "initializing large server to client packet. Size: %u, cmd: %u", size, cmd); - header[headerIndex++] = 0x80 | (0xFF & (size >> 16)); - } - header[headerIndex++] = 0xFF &(size >> 8); - header[headerIndex++] = 0xFF & size; - - header[headerIndex++] = 0xFF & cmd; - header[headerIndex++] = 0xFF & (cmd >> 8); - } - - uint8 getHeaderLength() - { - // cmd = 2 bytes, size= 2||3bytes - return 2 + (isLargePacket() ? 3 : 2); - } - - bool isLargePacket() const - { - return size > 0x7FFF; - } - - const uint32 size; - uint8 header[5]; -}; - -struct ClientPktHeader -{ - uint16 size; - uint32 cmd; -}; - -#if defined(__GNUC__) -#pragma pack() -#else -#pragma pack(pop) -#endif - -WorldSocket::WorldSocket (void): WorldHandler(), -m_LastPingTime(ACE_Time_Value::zero), m_OverSpeedPings(0), m_Session(0), -m_RecvWPct(0), m_RecvPct(), m_Header(sizeof (ClientPktHeader)), -m_OutBuffer(0), m_OutBufferSize(65536), m_OutActive(false), -m_Seed(static_cast<uint32> (rand32())) -{ - reference_counting_policy().value (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); - - msg_queue()->high_water_mark(8 * 1024 * 1024); - msg_queue()->low_water_mark(8 * 1024 * 1024); -} - -WorldSocket::~WorldSocket (void) -{ - delete m_RecvWPct; - - if (m_OutBuffer) - m_OutBuffer->release(); - - closing_ = true; - - peer().close(); -} - -bool WorldSocket::IsClosed (void) const -{ - return closing_; -} - -void WorldSocket::CloseSocket (void) -{ - { - ACE_GUARD (LockType, Guard, m_OutBufferLock); - - if (closing_) - return; - - closing_ = true; - peer().close_writer(); - } - - { - ACE_GUARD (LockType, Guard, m_SessionLock); - - m_Session = NULL; - } -} - -const std::string& WorldSocket::GetRemoteAddress (void) const -{ - return m_Address; -} - -int WorldSocket::SendPacket(WorldPacket const& pct) -{ - ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1); - - if (closing_) - return -1; - - // Dump outgoing packet - if (sPacketLog->CanLogPacket()) - sPacketLog->LogPacket(pct, SERVER_TO_CLIENT); - - WorldPacket const* pkt = &pct; - - - if (m_Session) - TC_LOG_TRACE("network.opcode", "S->C: %s %s", m_Session->GetPlayerInfo().c_str(), GetOpcodeNameForLogging(pkt->GetOpcode()).c_str()); - - sScriptMgr->OnPacketSend(this, *pkt); - - ServerPktHeader header(pkt->size()+2, pkt->GetOpcode()); - m_Crypt.EncryptSend ((uint8*)header.header, header.getHeaderLength()); - - if (m_OutBuffer->space() >= pkt->size() + header.getHeaderLength() && msg_queue()->is_empty()) - { - // Put the packet on the buffer. - if (m_OutBuffer->copy((char*) header.header, header.getHeaderLength()) == -1) - ACE_ASSERT (false); - - if (!pkt->empty()) - if (m_OutBuffer->copy((char*) pkt->contents(), pkt->size()) == -1) - ACE_ASSERT (false); - } - else - { - // Enqueue the packet. - ACE_Message_Block* mb; - - ACE_NEW_RETURN(mb, ACE_Message_Block(pkt->size() + header.getHeaderLength()), -1); - - mb->copy((char*) header.header, header.getHeaderLength()); - - if (!pkt->empty()) - mb->copy((const char*)pkt->contents(), pkt->size()); - - if (msg_queue()->enqueue_tail(mb, (ACE_Time_Value*)&ACE_Time_Value::zero) == -1) - { - TC_LOG_ERROR("network", "WorldSocket::SendPacket enqueue_tail failed"); - mb->release(); - return -1; - } - } - - return 0; -} - -long WorldSocket::AddReference (void) -{ - return static_cast<long> (add_reference()); -} - -long WorldSocket::RemoveReference (void) -{ - return static_cast<long> (remove_reference()); -} - -int WorldSocket::open (void *a) -{ - ACE_UNUSED_ARG (a); - - // Prevent double call to this func. - if (m_OutBuffer) - return -1; - - // This will also prevent the socket from being Updated - // while we are initializing it. - m_OutActive = true; - - // Hook for the manager. - if (sWorldSocketMgr->OnSocketOpen(this) == -1) - return -1; - - // Allocate the buffer. - ACE_NEW_RETURN (m_OutBuffer, ACE_Message_Block (m_OutBufferSize), -1); - - // Store peer address. - ACE_INET_Addr remote_addr; - - if (peer().get_remote_addr(remote_addr) == -1) - { - TC_LOG_ERROR("network", "WorldSocket::open: peer().get_remote_addr errno = %s", ACE_OS::strerror (errno)); - return -1; - } - - m_Address = remote_addr.get_host_addr(); - - if (HandleSendAuthSession() == -1) - return -1; - - // Register with ACE Reactor - if (reactor()->register_handler(this, ACE_Event_Handler::READ_MASK | ACE_Event_Handler::WRITE_MASK) == -1) - { - TC_LOG_ERROR("network", "WorldSocket::open: unable to register client handler errno = %s", ACE_OS::strerror (errno)); - return -1; - } - - // reactor takes care of the socket from now on - remove_reference(); - - return 0; -} - -int WorldSocket::close(u_long) -{ - shutdown(); - - closing_ = true; - - remove_reference(); - - return 0; -} - -int WorldSocket::handle_input(ACE_HANDLE) -{ - if (closing_) - return -1; - - switch (handle_input_missing_data()) - { - case -1 : - { - if ((errno == EWOULDBLOCK) || - (errno == EAGAIN)) - { - return Update(); // interesting line, isn't it ? - } - - TC_LOG_DEBUG("network", "WorldSocket::handle_input: Peer error closing connection errno = %s", ACE_OS::strerror (errno)); - - errno = ECONNRESET; - return -1; - } - case 0: - { - TC_LOG_DEBUG("network", "WorldSocket::handle_input: Peer has closed connection"); - - errno = ECONNRESET; - return -1; - } - case 1: - return 1; - default: - return Update(); // another interesting line ;) - } - - ACE_NOTREACHED(return -1); -} - -int WorldSocket::handle_output(ACE_HANDLE) -{ - ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1); - - if (closing_) - return -1; - - size_t send_len = m_OutBuffer->length(); - - if (send_len == 0) - return handle_output_queue(Guard); - -#ifdef MSG_NOSIGNAL - ssize_t n = peer().send (m_OutBuffer->rd_ptr(), send_len, MSG_NOSIGNAL); -#else - ssize_t n = peer().send (m_OutBuffer->rd_ptr(), send_len); -#endif // MSG_NOSIGNAL - - if (n == 0) - return -1; - else if (n == -1) - { - if (errno == EWOULDBLOCK || errno == EAGAIN) - return schedule_wakeup_output (Guard); - - return -1; - } - else if (n < (ssize_t)send_len) //now n > 0 - { - m_OutBuffer->rd_ptr (static_cast<size_t> (n)); - - // move the data to the base of the buffer - m_OutBuffer->crunch(); - - return schedule_wakeup_output (Guard); - } - else //now n == send_len - { - m_OutBuffer->reset(); - - return handle_output_queue (Guard); - } - - ACE_NOTREACHED (return 0); -} - -int WorldSocket::handle_output_queue(GuardType& g) -{ - if (msg_queue()->is_empty()) - return cancel_wakeup_output(g); - - ACE_Message_Block* mblk; - - if (msg_queue()->dequeue_head(mblk, (ACE_Time_Value*)&ACE_Time_Value::zero) == -1) - { - TC_LOG_ERROR("network", "WorldSocket::handle_output_queue dequeue_head"); - return -1; - } - - const size_t send_len = mblk->length(); - -#ifdef MSG_NOSIGNAL - ssize_t n = peer().send(mblk->rd_ptr(), send_len, MSG_NOSIGNAL); -#else - ssize_t n = peer().send(mblk->rd_ptr(), send_len); -#endif // MSG_NOSIGNAL - - if (n == 0) - { - mblk->release(); - - return -1; - } - else if (n == -1) - { - if (errno == EWOULDBLOCK || errno == EAGAIN) - { - msg_queue()->enqueue_head(mblk, (ACE_Time_Value*) &ACE_Time_Value::zero); - return schedule_wakeup_output (g); - } - - mblk->release(); - return -1; - } - else if (n < (ssize_t)send_len) //now n > 0 - { - mblk->rd_ptr(static_cast<size_t> (n)); - - if (msg_queue()->enqueue_head(mblk, (ACE_Time_Value*) &ACE_Time_Value::zero) == -1) - { - TC_LOG_ERROR("network", "WorldSocket::handle_output_queue enqueue_head"); - mblk->release(); - return -1; - } - - return schedule_wakeup_output (g); - } - else //now n == send_len - { - mblk->release(); - - return msg_queue()->is_empty() ? cancel_wakeup_output(g) : ACE_Event_Handler::WRITE_MASK; - } - - ACE_NOTREACHED(return -1); -} - -int WorldSocket::handle_close(ACE_HANDLE h, ACE_Reactor_Mask) -{ - // Critical section - { - ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1); - - closing_ = true; - - if (h == ACE_INVALID_HANDLE) - peer().close_writer(); - } - - // Critical section - { - ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1); - - m_Session = NULL; - } - - reactor()->remove_handler(this, ACE_Event_Handler::DONT_CALL | ACE_Event_Handler::ALL_EVENTS_MASK); - return 0; -} - -int WorldSocket::Update (void) -{ - if (closing_) - return -1; - - if (m_OutActive) - return 0; - - { - ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, 0); - if (m_OutBuffer->length() == 0 && msg_queue()->is_empty()) - return 0; - } - - int ret; - do - ret = handle_output(get_handle()); - while (ret > 0); - - return ret; -} - -int WorldSocket::handle_input_header (void) -{ - ACE_ASSERT(m_RecvWPct == NULL); - - ACE_ASSERT(m_Header.length() == sizeof(ClientPktHeader)); - - m_Crypt.DecryptRecv ((uint8*)m_Header.rd_ptr(), sizeof(ClientPktHeader)); - - ClientPktHeader& header = *((ClientPktHeader*)m_Header.rd_ptr()); - - EndianConvertReverse(header.size); - EndianConvert(header.cmd); - - if ((header.size < 4) || (header.size > 10240) || (header.cmd > 10240)) - { - Player* _player = m_Session ? m_Session->GetPlayer() : NULL; - TC_LOG_ERROR("network", "WorldSocket::handle_input_header(): client (account: %u, char [GUID: %u, name: %s]) sent malformed packet (size: %d, cmd: %d)", - m_Session ? m_Session->GetAccountId() : 0, - _player ? _player->GetGUIDLow() : 0, - _player ? _player->GetName().c_str() : "<none>", - header.size, header.cmd); - - errno = EINVAL; - return -1; - } - - header.size -= 4; - - ACE_NEW_RETURN(m_RecvWPct, WorldPacket ((uint16)header.cmd, header.size), -1); - - if (header.size > 0) - { - m_RecvWPct->resize(header.size); - m_RecvPct.base ((char*) m_RecvWPct->contents(), m_RecvWPct->size()); - } - else - { - ACE_ASSERT(m_RecvPct.space() == 0); - } - - return 0; -} - -int WorldSocket::handle_input_payload (void) -{ - // set errno properly here on error !!! - // now have a header and payload - - ACE_ASSERT (m_RecvPct.space() == 0); - ACE_ASSERT (m_Header.space() == 0); - ACE_ASSERT (m_RecvWPct != NULL); - - const int ret = ProcessIncoming (m_RecvWPct); - - m_RecvPct.base (NULL, 0); - m_RecvPct.reset(); - m_RecvWPct = NULL; - - m_Header.reset(); - - if (ret == -1) - errno = EINVAL; - - return ret; -} - -int WorldSocket::handle_input_missing_data (void) -{ - char buf [4096]; - - ACE_Data_Block db (sizeof (buf), - ACE_Message_Block::MB_DATA, - buf, - 0, - 0, - ACE_Message_Block::DONT_DELETE, - 0); - - ACE_Message_Block message_block(&db, - ACE_Message_Block::DONT_DELETE, - 0); - - const size_t recv_size = message_block.space(); - - const ssize_t n = peer().recv (message_block.wr_ptr(), - recv_size); - - if (n <= 0) - return int(n); - - message_block.wr_ptr (n); - - while (message_block.length() > 0) - { - if (m_Header.space() > 0) - { - //need to receive the header - const size_t to_header = (message_block.length() > m_Header.space() ? m_Header.space() : message_block.length()); - m_Header.copy (message_block.rd_ptr(), to_header); - message_block.rd_ptr (to_header); - - if (m_Header.space() > 0) - { - // Couldn't receive the whole header this time. - ACE_ASSERT (message_block.length() == 0); - errno = EWOULDBLOCK; - return -1; - } - - // We just received nice new header - if (handle_input_header() == -1) - { - ACE_ASSERT ((errno != EWOULDBLOCK) && (errno != EAGAIN)); - return -1; - } - } - - // Its possible on some error situations that this happens - // for example on closing when epoll receives more chunked data and stuff - // hope this is not hack, as proper m_RecvWPct is asserted around - if (!m_RecvWPct) - { - TC_LOG_ERROR("network", "Forcing close on input m_RecvWPct = NULL"); - errno = EINVAL; - return -1; - } - - // We have full read header, now check the data payload - if (m_RecvPct.space() > 0) - { - //need more data in the payload - const size_t to_data = (message_block.length() > m_RecvPct.space() ? m_RecvPct.space() : message_block.length()); - m_RecvPct.copy (message_block.rd_ptr(), to_data); - message_block.rd_ptr (to_data); - - if (m_RecvPct.space() > 0) - { - // Couldn't receive the whole data this time. - ACE_ASSERT (message_block.length() == 0); - errno = EWOULDBLOCK; - return -1; - } - } - - //just received fresh new payload - if (handle_input_payload() == -1) - { - ACE_ASSERT ((errno != EWOULDBLOCK) && (errno != EAGAIN)); - return -1; - } - } - - return size_t(n) == recv_size ? 1 : 2; -} - -int WorldSocket::cancel_wakeup_output(GuardType& g) -{ - if (!m_OutActive) - return 0; - - m_OutActive = false; - - g.release(); - - if (reactor()->cancel_wakeup - (this, ACE_Event_Handler::WRITE_MASK) == -1) - { - // would be good to store errno from reactor with errno guard - TC_LOG_ERROR("network", "WorldSocket::cancel_wakeup_output"); - return -1; - } - - return 0; -} - -int WorldSocket::schedule_wakeup_output(GuardType& g) -{ - if (m_OutActive) - return 0; - - m_OutActive = true; - - g.release(); - - if (reactor()->schedule_wakeup - (this, ACE_Event_Handler::WRITE_MASK) == -1) - { - TC_LOG_ERROR("network", "WorldSocket::schedule_wakeup_output"); - return -1; - } - - return 0; -} - -int WorldSocket::ProcessIncoming(WorldPacket* new_pct) -{ - ACE_ASSERT (new_pct); - - // manage memory ;) - ACE_Auto_Ptr<WorldPacket> aptr(new_pct); - - const ACE_UINT16 opcode = new_pct->GetOpcode(); - - if (closing_) - return -1; - - // Dump received packet. - if (sPacketLog->CanLogPacket()) - sPacketLog->LogPacket(*new_pct, CLIENT_TO_SERVER); - - std::string opcodeName = GetOpcodeNameForLogging(opcode); - if (m_Session) - TC_LOG_TRACE("network.opcode", "C->S: %s %s", m_Session->GetPlayerInfo().c_str(), opcodeName.c_str()); - - try - { - switch (opcode) - { - case CMSG_PING: - return HandlePing(*new_pct); - case CMSG_AUTH_SESSION: - if (m_Session) - { - TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s", m_Session->GetPlayerInfo().c_str()); - return -1; - } - - sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct)); - return HandleAuthSession(*new_pct); - case CMSG_KEEP_ALIVE: - TC_LOG_DEBUG("network", "%s", opcodeName.c_str()); - sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct)); - return 0; - default: - { - ACE_GUARD_RETURN(LockType, Guard, m_SessionLock, -1); - if (!m_Session) - { - TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); - return -1; - } - - // Our Idle timer will reset on any non PING opcodes. - // Catches people idling on the login screen and any lingering ingame connections. - m_Session->ResetTimeOutTime(); - - // OK, give the packet to WorldSession - aptr.release(); - // WARNING here we call it with locks held. - // Its possible to cause deadlock if QueuePacket calls back - m_Session->QueuePacket(new_pct); - return 0; - } - } - } - catch (ByteBufferException &) - { - TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming ByteBufferException occured while parsing an instant handled packet %s from client %s, accountid=%i. Disconnected client.", - opcodeName.c_str(), GetRemoteAddress().c_str(), m_Session ? int32(m_Session->GetAccountId()) : -1); - new_pct->hexlike(); - return -1; - } - - ACE_NOTREACHED (return 0); -} - -int WorldSocket::HandleSendAuthSession() -{ - WorldPacket packet(SMSG_AUTH_CHALLENGE, 37); - packet << uint32(1); // 1...31 - packet << uint32(m_Seed); - - BigNumber seed1; - seed1.SetRand(16 * 8); - packet.append(seed1.AsByteArray(16).get(), 16); // new encryption seeds - - BigNumber seed2; - seed2.SetRand(16 * 8); - packet.append(seed2.AsByteArray(16).get(), 16); // new encryption seeds - return SendPacket(packet); -} - -int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) -{ - uint8 digest[20]; - uint32 clientSeed; - uint8 security; - uint32 id; - LocaleConstant locale; - std::string account; - SHA1Hash sha; - uint32 clientBuild; - uint32 unk2, unk3, unk5, unk6, unk7; - 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).", GetRemoteAddress().c_str()); - return -1; - } - - // Read the content of the packet - recvPacket >> clientBuild; - recvPacket >> unk2; - recvPacket >> account; - recvPacket >> unk3; - recvPacket >> clientSeed; - recvPacket >> unk5 >> unk6 >> unk7; - recvPacket >> unk4; - recvPacket.read(digest, 20); - - TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, unk3 %u, clientseed %u", - clientBuild, - unk2, - account.c_str(), - unk3, - clientSeed); - - // Get the account information from the realmd database - // 0 1 2 3 4 5 6 7 8 - // SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ? - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME); - - stmt->setString(0, account); - - PreparedQueryResult result = LoginDatabase.Query(stmt); - - // Stop if the account is not found - if (!result) - { - // We can not log here, as we do not know the account. Thus, no accountId. - SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account)."); - return -1; - } - - Field* fields = result->Fetch(); - - uint8 expansion = fields[4].GetUInt8(); - uint32 world_expansion = sWorld->getIntConfig(CONFIG_EXPANSION); - if (expansion > world_expansion) - expansion = world_expansion; - - // For hook purposes, we get Remoteaddress at this point. - std::string address = GetRemoteAddress(); - - // As we don't know if attempted login process by ip works, we update last_attempt_ip right away - stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP); - - stmt->setString(0, address); - stmt->setString(1, account); - - LoginDatabase.Execute(stmt); - // This also allows to check for possible "hack" attempts on account - - // id has to be fetched at this point, so that first actual account response that fails can be logged - id = fields[0].GetUInt32(); - - ///- Re-check ip locking (same check as in realmd). - if (fields[3].GetUInt8() == 1) // if ip is locked - { - if (strcmp (fields[2].GetCString(), address.c_str())) - { - SendAuthResponseError(AUTH_FAILED); - TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s).", fields[2].GetCString(), address.c_str()); - // 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); - return -1; - } - } - - 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) - { - mutetime = time(NULL) + llabs(mutetime); - - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME_LOGIN); - - stmt->setInt64(0, mutetime); - stmt->setUInt32(1, id); - - LoginDatabase.Execute(stmt); - } - - locale = LocaleConstant (fields[6].GetUInt8()); - if (locale >= TOTAL_LOCALES) - 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 -1; - } - - // Checks gmlevel per Realm - stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); - - stmt->setUInt32(0, id); - stmt->setInt32(1, int32(realmID)); - - result = LoginDatabase.Query(stmt); - - if (!result) - security = 0; - else - { - fields = result->Fetch(); - security = fields[0].GetUInt8(); - } - - // Re-check account ban (same check as in realmd) - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BANS); - - stmt->setUInt32(0, id); - stmt->setString(1, address); - - PreparedQueryResult banresult = LoginDatabase.Query(stmt); - - if (banresult) // if account banned - { - SendAuthResponseError(AUTH_BANNED); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)."); - sScriptMgr->OnFailedAccountLogin(id); - return -1; - } - - // Check locked state for server - AccountTypes allowedAccountType = sWorld->GetPlayerSecurityLimit(); - TC_LOG_DEBUG("network", "Allowed Level: %u Player Level %u", allowedAccountType, AccountTypes(security)); - if (allowedAccountType > SEC_PLAYER && AccountTypes(security) < allowedAccountType) - { - SendAuthResponseError(AUTH_UNAVAILABLE); - TC_LOG_INFO("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); - sScriptMgr->OnFailedAccountLogin(id); - return -1; - } - - // Check that Key and account name are the same on client and server - uint32 t = 0; - uint32 seed = m_Seed; - - sha.UpdateData(account); - sha.UpdateData((uint8*)&t, 4); - sha.UpdateData((uint8*)&clientSeed, 4); - sha.UpdateData((uint8*)&seed, 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()); - return -1; - } - - TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.", - account.c_str(), - address.c_str()); - - // Check if this user is by any chance a recruiter - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_RECRUITER); - - stmt->setUInt32(0, id); - - result = LoginDatabase.Query(stmt); - - bool isRecruiter = false; - if (result) - isRecruiter = true; - - // Update the last_ip in the database as it was successful for login - stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_IP); - - stmt->setString(0, address); - stmt->setString(1, account); - - LoginDatabase.Execute(stmt); - - // NOTE ATM the socket is single-threaded, have this in mind ... - ACE_NEW_RETURN(m_Session, WorldSession(id, this, AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter), -1); - - m_Crypt.Init(&k); - - m_Session->LoadGlobalAccountData(); - m_Session->LoadTutorialsData(); - m_Session->ReadAddonsInfo(recvPacket); - m_Session->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) - m_Session->InitWarden(&k, os); - - // Sleep this Network thread for - uint32 sleepTime = sWorld->getIntConfig(CONFIG_SESSION_ADD_DELAY); - ACE_OS::sleep(ACE_Time_Value(0, sleepTime)); - - sWorld->AddSession(m_Session); - return 0; -} - -int WorldSocket::HandlePing(WorldPacket& recvPacket) -{ - uint32 ping; - uint32 latency; - - // Get the ping packet content - recvPacket >> ping; - recvPacket >> latency; - - if (m_LastPingTime == ACE_Time_Value::zero) - m_LastPingTime = ACE_OS::gettimeofday(); // for 1st ping - else - { - ACE_Time_Value cur_time = ACE_OS::gettimeofday(); - ACE_Time_Value diff_time (cur_time); - diff_time -= m_LastPingTime; - m_LastPingTime = cur_time; - - if (diff_time < ACE_Time_Value (27)) - { - ++m_OverSpeedPings; - - uint32 max_count = sWorld->getIntConfig (CONFIG_MAX_OVERSPEED_PINGS); - - if (max_count && m_OverSpeedPings > max_count) - { - ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1); - - if (m_Session && !m_Session->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_OVERSPEED_PING)) - { - TC_LOG_ERROR("network", "WorldSocket::HandlePing: %s kicked for over-speed pings (address: %s)", - m_Session->GetPlayerInfo().c_str(), GetRemoteAddress().c_str()); - - return -1; - } - } - } - else - m_OverSpeedPings = 0; - } - - // critical section - { - ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1); - - if (m_Session) - { - m_Session->SetLatency (latency); - m_Session->ResetClientTimeDelay(); - } - else - { - TC_LOG_ERROR("network", "WorldSocket::HandlePing: peer sent CMSG_PING, " - "but is not authenticated or got recently kicked, " - " address = %s", - GetRemoteAddress().c_str()); - return -1; - } - } - - WorldPacket packet(SMSG_PONG, 4); - packet << ping; - return SendPacket(packet); -} - -void WorldSocket::SendAuthResponseError(uint8 code) -{ - WorldPacket packet(SMSG_AUTH_RESPONSE, 1); - packet << uint8(code); - SendPacket(packet); -} diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h deleted file mode 100644 index 1f98be0152b..00000000000 --- a/src/server/game/Server/WorldSocket.h +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/** \addtogroup u2w User to World Communication - * @{ - * \file WorldSocket.h - * \author Derex <derex101@gmail.com> - */ - -#ifndef _WORLDSOCKET_H -#define _WORLDSOCKET_H - -#include <ace/Basic_Types.h> -#include <ace/Synch_Traits.h> -#include <ace/Svc_Handler.h> -#include <ace/SOCK_Stream.h> -#include <ace/Thread_Mutex.h> -#include <ace/Guard_T.h> -#include <ace/Unbounded_Queue.h> -#include <ace/Message_Block.h> - -#if !defined (ACE_LACKS_PRAGMA_ONCE) -#pragma once -#endif /* ACE_LACKS_PRAGMA_ONCE */ - -#include "Common.h" -#include "AuthCrypt.h" - -class ACE_Message_Block; -class WorldPacket; -class WorldSession; - -/// Handler that can communicate over stream sockets. -typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> WorldHandler; - -/** - * WorldSocket. - * - * This class is responsible for the communication with - * remote clients. - * Most methods return -1 on failure. - * The class uses reference counting. - * - * For output the class uses one buffer (64K usually) and - * a queue where it stores packet if there is no place on - * the queue. The reason this is done, is because the server - * does really a lot of small-size writes to it, and it doesn't - * scale well to allocate memory for every. When something is - * written to the output buffer the socket is not immediately - * activated for output (again for the same reason), there - * is 10ms celling (thats why there is Update() method). - * This concept is similar to TCP_CORK, but TCP_CORK - * uses 200ms celling. As result overhead generated by - * sending packets from "producer" threads is minimal, - * and doing a lot of writes with small size is tolerated. - * - * The calls to Update() method are managed by WorldSocketMgr - * and ReactorRunnable. - * - * For input, the class uses one 4096 bytes buffer on stack - * to which it does recv() calls. And then received data is - * distributed where its needed. 4096 matches pretty well the - * traffic generated by client for now. - * - * The input/output do speculative reads/writes (AKA it tryes - * to read all data available in the kernel buffer or tryes to - * write everything available in userspace buffer), - * which is ok for using with Level and Edge Triggered IO - * notification. - * - */ -class WorldSocket : public WorldHandler -{ - public: - WorldSocket (void); - virtual ~WorldSocket (void); - - friend class WorldSocketMgr; - - /// Mutex type used for various synchronizations. - typedef ACE_Thread_Mutex LockType; - typedef ACE_Guard<LockType> GuardType; - - /// Check if socket is closed. - bool IsClosed(void) const; - - /// Close the socket. - void CloseSocket(void); - - /// Get address of connected peer. - const std::string& GetRemoteAddress(void) const; - - /// Send A packet on the socket, this function is reentrant. - /// @param pct packet to send - /// @return -1 of failure - int SendPacket(const WorldPacket& pct); - - /// Add reference to this object. - long AddReference(void); - - /// Remove reference to this object. - long RemoveReference(void); - - /// things called by ACE framework. - - /// Called on open, the void* is the acceptor. - virtual int open(void *); - - /// Called on failures inside of the acceptor, don't call from your code. - virtual int close(u_long); - - /// Called when we can read from the socket. - virtual int handle_input(ACE_HANDLE = ACE_INVALID_HANDLE); - - /// Called when the socket can write. - virtual int handle_output(ACE_HANDLE = ACE_INVALID_HANDLE); - - /// Called when connection is closed or error happens. - virtual int handle_close(ACE_HANDLE = ACE_INVALID_HANDLE, - ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK); - - /// Called by WorldSocketMgr/ReactorRunnable. - int Update(void); - - private: - /// Helper functions for processing incoming data. - int handle_input_header(void); - int handle_input_payload(void); - int handle_input_missing_data(void); - - /// Help functions to mark/unmark the socket for output. - /// @param g the guard is for m_OutBufferLock, the function will release it - int cancel_wakeup_output(GuardType& g); - int schedule_wakeup_output(GuardType& g); - - /// Drain the queue if its not empty. - int handle_output_queue(GuardType& g); - - /// process one incoming packet. - /// @param new_pct received packet, note that you need to delete it. - int ProcessIncoming(WorldPacket* new_pct); - - /// Called by ProcessIncoming() on CMSG_AUTH_SESSION. - int HandleAuthSession(WorldPacket& recvPacket); - - /// Called by ProcessIncoming() on CMSG_PING. - int HandlePing(WorldPacket& recvPacket); - - int HandleSendAuthSession(); - - private: - void SendAuthResponseError(uint8); - /// Time in which the last ping was received - ACE_Time_Value m_LastPingTime; - - /// Keep track of over-speed pings, to prevent ping flood. - uint32 m_OverSpeedPings; - - /// Address of the remote peer - std::string m_Address; - - /// Class used for managing encryption of the headers - AuthCrypt m_Crypt; - - /// Mutex lock to protect m_Session - LockType m_SessionLock; - - /// Session to which received packets are routed - WorldSession* m_Session; - - /// here are stored the fragments of the received data - WorldPacket* m_RecvWPct; - - /// This block actually refers to m_RecvWPct contents, - /// which allows easy and safe writing to it. - /// It wont free memory when its deleted. m_RecvWPct takes care of freeing. - ACE_Message_Block m_RecvPct; - - /// Fragment of the received header. - ACE_Message_Block m_Header; - - /// Mutex for protecting output related data. - LockType m_OutBufferLock; - - /// Buffer used for writing output. - ACE_Message_Block* m_OutBuffer; - - /// Size of the m_OutBuffer. - size_t m_OutBufferSize; - - /// True if the socket is registered with the reactor for output - bool m_OutActive; - - uint32 m_Seed; - - WorldSocket(WorldSocket const& right) = delete; - WorldSocket& operator=(WorldSocket const& right) = delete; -}; - -#endif /* _WORLDSOCKET_H */ - -/// @} - diff --git a/src/server/game/Server/WorldSocketAcceptor.h b/src/server/game/Server/WorldSocketAcceptor.h deleted file mode 100644 index 0b07196a0cd..00000000000 --- a/src/server/game/Server/WorldSocketAcceptor.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/** \addtogroup u2w User to World Communication - * @{ - * \file WorldSocketMgr.h - */ - -#ifndef __WORLDSOCKETACCEPTOR_H_ -#define __WORLDSOCKETACCEPTOR_H_ - -#include "Common.h" - -#include <ace/Acceptor.h> -#include <ace/SOCK_Acceptor.h> - -#include "WorldSocket.h" - -class WorldSocketAcceptor : public ACE_Acceptor<WorldSocket, ACE_SOCK_Acceptor> -{ -public: - WorldSocketAcceptor(void) { } - virtual ~WorldSocketAcceptor(void) - { - if (reactor()) - reactor()->cancel_timer(this, 1); - } - -protected: - - virtual int handle_timeout(const ACE_Time_Value& /*current_time*/, const void* /*act = 0*/) - { - TC_LOG_DEBUG("misc", "Resuming acceptor"); - reactor()->cancel_timer(this, 1); - return reactor()->register_handler(this, ACE_Event_Handler::ACCEPT_MASK); - } - - virtual int handle_accept_error(void) - { -#if defined(ENFILE) && defined(EMFILE) - if (errno == ENFILE || errno == EMFILE) - { - TC_LOG_ERROR("misc", "Out of file descriptors, suspending incoming connections for 10 seconds"); - reactor()->remove_handler(this, ACE_Event_Handler::ACCEPT_MASK | ACE_Event_Handler::DONT_CALL); - reactor()->schedule_timer(this, NULL, ACE_Time_Value(10)); - } -#endif - return 0; - } -}; - -#endif /* __WORLDSOCKETACCEPTOR_H_ */ -/// @} diff --git a/src/server/game/Server/WorldSocketMgr.cpp b/src/server/game/Server/WorldSocketMgr.cpp deleted file mode 100644 index 3c1db3551d0..00000000000 --- a/src/server/game/Server/WorldSocketMgr.cpp +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/** \file WorldSocketMgr.cpp -* \ingroup u2w -* \author Derex <derex101@gmail.com> -*/ - -#include "WorldSocketMgr.h" - -#include <ace/ACE.h> -#include <ace/Log_Msg.h> -#include <ace/Reactor.h> -#include <ace/Reactor_Impl.h> -#include <ace/TP_Reactor.h> -#include <ace/Dev_Poll_Reactor.h> -#include <ace/Atomic_Op.h> -#include <ace/os_include/arpa/os_inet.h> -#include <ace/os_include/netinet/os_tcp.h> -#include <ace/os_include/sys/os_types.h> -#include <ace/os_include/sys/os_socket.h> - -#include <mutex> -#include <set> - -#include "Log.h" -#include "Common.h" -#include "Config.h" -#include "DatabaseEnv.h" -#include "WorldSocket.h" -#include "WorldSocketAcceptor.h" -#include "ScriptMgr.h" - -/** -* This is a helper class to WorldSocketMgr, that manages -* network threads, and assigning connections from acceptor thread -* to other network threads -*/ -class ReactorRunnable : protected ACE_Task_Base -{ - public: - - ReactorRunnable() : - m_Reactor(0), - m_Connections(0), - m_ThreadId(-1) - { - ACE_Reactor_Impl* imp; - - #if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL) - - imp = new ACE_Dev_Poll_Reactor(); - - imp->max_notify_iterations (128); - imp->restart (1); - - #else - - imp = new ACE_TP_Reactor(); - imp->max_notify_iterations (128); - - #endif - - m_Reactor = new ACE_Reactor (imp, 1); - } - - virtual ~ReactorRunnable() - { - Stop(); - Wait(); - - delete m_Reactor; - } - - void Stop() - { - m_Reactor->end_reactor_event_loop(); - } - - int Start() - { - if (m_ThreadId != -1) - return -1; - - return (m_ThreadId = activate()); - } - - void Wait() { ACE_Task_Base::wait(); } - - long Connections() - { - return static_cast<long> (m_Connections.value()); - } - - int AddSocket (WorldSocket* sock) - { - std::lock_guard<std::mutex> lock(newSocketsLock); - - ++m_Connections; - sock->AddReference(); - sock->reactor (m_Reactor); - m_NewSockets.insert (sock); - - sScriptMgr->OnSocketOpen(sock); - - return 0; - } - - ACE_Reactor* GetReactor() - { - return m_Reactor; - } - - protected: - - void AddNewSockets() - { - std::lock_guard<std::mutex> lock(newSocketsLock); - - if (m_NewSockets.empty()) - return; - - for (SocketSet::const_iterator i = m_NewSockets.begin(); i != m_NewSockets.end(); ++i) - { - WorldSocket* sock = (*i); - - if (sock->IsClosed()) - { - sScriptMgr->OnSocketClose(sock, true); - - sock->RemoveReference(); - --m_Connections; - } - else - m_Sockets.insert (sock); - } - - m_NewSockets.clear(); - } - - virtual int svc() - { - TC_LOG_DEBUG("misc", "Network Thread Starting"); - - ACE_ASSERT (m_Reactor); - - SocketSet::iterator i, t; - - while (!m_Reactor->reactor_event_loop_done()) - { - // dont be too smart to move this outside the loop - // the run_reactor_event_loop will modify interval - ACE_Time_Value interval (0, 10000); - - if (m_Reactor->run_reactor_event_loop (interval) == -1) - break; - - AddNewSockets(); - - for (i = m_Sockets.begin(); i != m_Sockets.end();) - { - if ((*i)->Update() == -1) - { - t = i; - ++i; - - (*t)->CloseSocket(); - - sScriptMgr->OnSocketClose((*t), false); - - (*t)->RemoveReference(); - --m_Connections; - m_Sockets.erase (t); - } - else - ++i; - } - } - - TC_LOG_DEBUG("misc", "Network Thread exits"); - - return 0; - } - - private: - typedef ACE_Atomic_Op<ACE_SYNCH_MUTEX, long> AtomicInt; - typedef std::set<WorldSocket*> SocketSet; - - ACE_Reactor* m_Reactor; - AtomicInt m_Connections; - int m_ThreadId; - - SocketSet m_Sockets; - - SocketSet m_NewSockets; - std::mutex newSocketsLock; -}; - -WorldSocketMgr::WorldSocketMgr() : - m_NetThreads(0), - m_NetThreadsCount(0), - m_SockOutKBuff(-1), - m_SockOutUBuff(65536), - m_UseNoDelay(true), - m_Acceptor (0) { } - -WorldSocketMgr::~WorldSocketMgr() -{ - delete [] m_NetThreads; - delete m_Acceptor; -} - -int -WorldSocketMgr::StartReactiveIO (ACE_UINT16 port, const char* address) -{ - m_UseNoDelay = sConfigMgr->GetBoolDefault ("Network.TcpNodelay", true); - - int num_threads = sConfigMgr->GetIntDefault ("Network.Threads", 1); - - if (num_threads <= 0) - { - TC_LOG_ERROR("misc", "Network.Threads is wrong in your config file"); - return -1; - } - - m_NetThreadsCount = static_cast<size_t> (num_threads + 1); - - m_NetThreads = new ReactorRunnable[m_NetThreadsCount]; - - TC_LOG_DEBUG("misc", "Max allowed socket connections %d", ACE::max_handles()); - - // -1 means use default - m_SockOutKBuff = sConfigMgr->GetIntDefault ("Network.OutKBuff", -1); - - m_SockOutUBuff = sConfigMgr->GetIntDefault ("Network.OutUBuff", 65536); - - if (m_SockOutUBuff <= 0) - { - TC_LOG_ERROR("misc", "Network.OutUBuff is wrong in your config file"); - return -1; - } - - m_Acceptor = new WorldSocketAcceptor; - - ACE_INET_Addr listen_addr (port, address); - - if (m_Acceptor->open(listen_addr, m_NetThreads[0].GetReactor(), ACE_NONBLOCK) == -1) - { - TC_LOG_ERROR("misc", "Failed to open acceptor, check if the port is free"); - return -1; - } - - for (size_t i = 0; i < m_NetThreadsCount; ++i) - m_NetThreads[i].Start(); - - return 0; -} - -int -WorldSocketMgr::StartNetwork (ACE_UINT16 port, const char* address) -{ - if (!sLog->ShouldLog("misc", LOG_LEVEL_DEBUG)) - ACE_Log_Msg::instance()->priority_mask (LM_ERROR, ACE_Log_Msg::PROCESS); - - if (StartReactiveIO(port, address) == -1) - return -1; - - sScriptMgr->OnNetworkStart(); - - return 0; -} - -void -WorldSocketMgr::StopNetwork() -{ - if (m_Acceptor) - { - m_Acceptor->close(); - } - - if (m_NetThreadsCount != 0) - { - for (size_t i = 0; i < m_NetThreadsCount; ++i) - m_NetThreads[i].Stop(); - } - - Wait(); - - sScriptMgr->OnNetworkStop(); -} - -void -WorldSocketMgr::Wait() -{ - if (m_NetThreadsCount != 0) - { - for (size_t i = 0; i < m_NetThreadsCount; ++i) - m_NetThreads[i].Wait(); - } -} - -int -WorldSocketMgr::OnSocketOpen (WorldSocket* sock) -{ - // set some options here - if (m_SockOutKBuff >= 0) - { - if (sock->peer().set_option (SOL_SOCKET, - SO_SNDBUF, - (void*) & m_SockOutKBuff, - sizeof (int)) == -1 && errno != ENOTSUP) - { - TC_LOG_ERROR("misc", "WorldSocketMgr::OnSocketOpen set_option SO_SNDBUF"); - return -1; - } - } - - static const int ndoption = 1; - - // Set TCP_NODELAY. - if (m_UseNoDelay) - { - if (sock->peer().set_option (ACE_IPPROTO_TCP, - TCP_NODELAY, - (void*)&ndoption, - sizeof (int)) == -1) - { - TC_LOG_ERROR("misc", "WorldSocketMgr::OnSocketOpen: peer().set_option TCP_NODELAY errno = %s", ACE_OS::strerror (errno)); - return -1; - } - } - - sock->m_OutBufferSize = static_cast<size_t> (m_SockOutUBuff); - - // we skip the Acceptor Thread - size_t min = 1; - - ACE_ASSERT (m_NetThreadsCount >= 1); - - for (size_t i = 1; i < m_NetThreadsCount; ++i) - if (m_NetThreads[i].Connections() < m_NetThreads[min].Connections()) - min = i; - - return m_NetThreads[min].AddSocket (sock); -} diff --git a/src/server/game/Server/WorldSocketMgr.h b/src/server/game/Server/WorldSocketMgr.h deleted file mode 100644 index 2c91a164ff9..00000000000 --- a/src/server/game/Server/WorldSocketMgr.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/** \addtogroup u2w User to World Communication - * @{ - * \file WorldSocketMgr.h - * \author Derex <derex101@gmail.com> - */ - -#ifndef __WORLDSOCKETMGR_H -#define __WORLDSOCKETMGR_H - -#include <ace/Basic_Types.h> -#include <ace/Thread_Mutex.h> - -class WorldSocket; -class ReactorRunnable; -class ACE_Event_Handler; - -/// Manages all sockets connected to peers and network threads -class WorldSocketMgr -{ - friend class WorldSocket; - -public: - static WorldSocketMgr* instance() - { - static WorldSocketMgr* instance = new WorldSocketMgr(); - return instance; - } - - /// Start network, listen at address:port . - int StartNetwork(ACE_UINT16 port, const char* address); - - /// Stops all network threads, It will wait for all running threads . - void StopNetwork(); - - /// Wait untill all network threads have "joined" . - void Wait(); - -private: - int OnSocketOpen(WorldSocket* sock); - - int StartReactiveIO(ACE_UINT16 port, const char* address); - -private: - WorldSocketMgr(); - virtual ~WorldSocketMgr(); - - ReactorRunnable* m_NetThreads; - size_t m_NetThreadsCount; - - int m_SockOutKBuff; - int m_SockOutUBuff; - bool m_UseNoDelay; - - class WorldSocketAcceptor* m_Acceptor; -}; - -#define sWorldSocketMgr WorldSocketMgr::instance() - -#endif -/// @} diff --git a/src/server/game/Server/WorldTcpSession.cpp b/src/server/game/Server/WorldTcpSession.cpp new file mode 100644 index 00000000000..0efd7ab8042 --- /dev/null +++ b/src/server/game/Server/WorldTcpSession.cpp @@ -0,0 +1,407 @@ +/* +* Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> +* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <boost/asio/write.hpp> +#include <boost/asio/read_until.hpp> +#include "WorldTcpSession.h" +#include "ServerPktHeader.h" +#include "BigNumber.h" +#include "Opcodes.h" +#include "ScriptMgr.h" +#include "SHA1.h" + +using boost::asio::ip::tcp; +using boost::asio::streambuf; + +void WorldTcpSession::Start() +{ + AsyncReadHeader(); + HandleSendAuthSession(); +} + +void WorldTcpSession::HandleSendAuthSession() +{ + WorldPacket packet(SMSG_AUTH_CHALLENGE, 37); + packet << uint32(1); // 1...31 + packet << uint32(_authSeed); + + BigNumber seed1; + seed1.SetRand(16 * 8); + packet.append(seed1.AsByteArray(16).get(), 16); // new encryption seeds + + BigNumber seed2; + seed2.SetRand(16 * 8); + packet.append(seed2.AsByteArray(16).get(), 16); // new encryption seeds + + AsyncWrite(packet); +} + + +void WorldTcpSession::AsyncReadHeader() +{ + auto self(shared_from_this()); + + _socket.async_read_some(boost::asio::buffer(_readBuffer, sizeof(ClientPktHeader)), [this, self](boost::system::error_code error, size_t transferedBytes) + { + if (!error && transferedBytes == sizeof(ClientPktHeader)) + { + ClientPktHeader* header = (ClientPktHeader*)&_readBuffer; + + EndianConvertReverse(header->size); + EndianConvert(header->cmd); + + AsyncReadData(header->size - sizeof(ClientPktHeader)); + } + else + { + _socket.close(); + } + }); +} + +void WorldTcpSession::AsyncReadData(size_t dataSize) +{ + auto self(shared_from_this()); + + _socket.async_read_some(boost::asio::buffer(&_readBuffer[sizeof(ClientPktHeader)], dataSize), [this, self, dataSize](boost::system::error_code error, size_t transferedBytes) + { + if (!error && transferedBytes == dataSize) + { + ClientPktHeader* header = (ClientPktHeader*)&_readBuffer; + + header->size -= sizeof(ClientPktHeader); + + uint16 opcode = (uint16)header->cmd; + + std::string opcodeName = GetOpcodeNameForLogging(opcode); + + WorldPacket* packet = new WorldPacket(opcode, header->size); + + packet->resize(header->size); + + std::memcpy(packet->contents(), &_readBuffer[sizeof(ClientPktHeader)], header->size); + + switch (opcode) + { + case CMSG_PING: + //return HandlePing(*new_pct); + break; + case CMSG_AUTH_SESSION: + if (_worldSession) + { + TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s", _worldSession->GetPlayerInfo().c_str()); + break; + } + + // sScriptMgr->OnPacketReceive(this, packet); + HandleAuthSession(*packet); + break; + case CMSG_KEEP_ALIVE: + TC_LOG_DEBUG("network", "%s", opcodeName.c_str()); + //sScriptMgr->OnPacketReceive(this, packet); + break; + default: + { + //ACE_GUARD_RETURN(LockType, Guard, m_SessionLock, -1); + + if (!_worldSession) + { + TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); + break; + } + + // Our Idle timer will reset on any non PING opcodes. + // Catches people idling on the login screen and any lingering ingame connections. + _worldSession->ResetTimeOutTime(); + + // WARNING here we call it with locks held. + // Its possible to cause deadlock if QueuePacket calls back + _worldSession->QueuePacket(packet); + + break; + } + } + + + AsyncReadHeader(); + } + else + { + _socket.close(); + } + }); +} + +void WorldTcpSession::AsyncWrite(WorldPacket const& packet) +{ + ServerPktHeader header(packet.size() + 2, packet.GetOpcode()); + _authCrypt.EncryptSend((uint8*)header.header, header.getHeaderLength()); + + std::memcpy(_writeBuffer, (char*)header.header, header.getHeaderLength()); + + std::memcpy(_writeBuffer + header.getHeaderLength(), (char const*)packet.contents(), packet.size()); + + boost::asio::async_write(_socket, boost::asio::buffer(_writeBuffer, header.getHeaderLength() + packet.size()), [this](boost::system::error_code error, std::size_t /*length*/) + { + if (error) + { + _socket.close(); + } + }); +} + +int WorldTcpSession::HandleAuthSession(WorldPacket& recvPacket) +{ + uint8 digest[20]; + uint32 clientSeed; + uint8 security; + uint32 id; + LocaleConstant locale; + std::string account; + SHA1Hash sha; + uint32 clientBuild; + uint32 unk2, unk3, unk5, unk6, unk7; + 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().c_str()); + return -1; + } + + // Read the content of the packet + recvPacket >> clientBuild; + recvPacket >> unk2; + recvPacket >> account; + recvPacket >> unk3; + recvPacket >> clientSeed; + recvPacket >> unk5 >> unk6 >> unk7; + recvPacket >> unk4; + recvPacket.read(digest, 20); + + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, unk3 %u, clientseed %u", + clientBuild, + unk2, + account.c_str(), + unk3, + clientSeed); + + // Get the account information from the realmd database + // 0 1 2 3 4 5 6 7 8 + // SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ? + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME); + + stmt->setString(0, account); + + PreparedQueryResult result = LoginDatabase.Query(stmt); + + // Stop if the account is not found + if (!result) + { + // We can not log here, as we do not know the account. Thus, no accountId. + SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account)."); + return -1; + } + + Field* fields = result->Fetch(); + + uint8 expansion = fields[4].GetUInt8(); + uint32 world_expansion = sWorld->getIntConfig(CONFIG_EXPANSION); + if (expansion > world_expansion) + expansion = world_expansion; + + // For hook purposes, we get Remoteaddress at this point. + std::string address = GetRemoteIpAddress(); + + // As we don't know if attempted login process by ip works, we update last_attempt_ip right away + stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP); + + stmt->setString(0, address); + stmt->setString(1, account); + + LoginDatabase.Execute(stmt); + // This also allows to check for possible "hack" attempts on account + + // id has to be fetched at this point, so that first actual account response that fails can be logged + id = fields[0].GetUInt32(); + + ///- Re-check ip locking (same check as in realmd). + if (fields[3].GetUInt8() == 1) // if ip is locked + { + if (strcmp(fields[2].GetCString(), address.c_str())) + { + SendAuthResponseError(AUTH_FAILED); + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s).", fields[2].GetCString(), address.c_str()); + // 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); + return -1; + } + } + + 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) + { + mutetime = time(NULL) + llabs(mutetime); + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME_LOGIN); + + stmt->setInt64(0, mutetime); + stmt->setUInt32(1, id); + + LoginDatabase.Execute(stmt); + } + + locale = LocaleConstant(fields[6].GetUInt8()); + if (locale >= TOTAL_LOCALES) + 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 -1; + } + + // Checks gmlevel per Realm + stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); + + stmt->setUInt32(0, id); + stmt->setInt32(1, int32(realmID)); + + result = LoginDatabase.Query(stmt); + + if (!result) + security = 0; + else + { + fields = result->Fetch(); + security = fields[0].GetUInt8(); + } + + // Re-check account ban (same check as in realmd) + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BANS); + + stmt->setUInt32(0, id); + stmt->setString(1, address); + + PreparedQueryResult banresult = LoginDatabase.Query(stmt); + + if (banresult) // if account banned + { + SendAuthResponseError(AUTH_BANNED); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)."); + sScriptMgr->OnFailedAccountLogin(id); + return -1; + } + + // Check locked state for server + AccountTypes allowedAccountType = sWorld->GetPlayerSecurityLimit(); + TC_LOG_DEBUG("network", "Allowed Level: %u Player Level %u", allowedAccountType, AccountTypes(security)); + if (allowedAccountType > SEC_PLAYER && AccountTypes(security) < allowedAccountType) + { + SendAuthResponseError(AUTH_UNAVAILABLE); + TC_LOG_INFO("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); + sScriptMgr->OnFailedAccountLogin(id); + return -1; + } + + // 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()); + return -1; + } + + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.", + account.c_str(), + address.c_str()); + + // Check if this user is by any chance a recruiter + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_RECRUITER); + + stmt->setUInt32(0, id); + + result = LoginDatabase.Query(stmt); + + bool isRecruiter = false; + if (result) + isRecruiter = true; + + // Update the last_ip in the database as it was successful for login + stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_IP); + + stmt->setString(0, address); + stmt->setString(1, account); + + LoginDatabase.Execute(stmt); + + // NOTE ATM the socket is single-threaded, have this in mind ... + _worldSession = new WorldSession(id, this, AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter); + + _authCrypt.Init(&k); + + _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); + + // Sleep this Network thread for + // uint32 sleepTime = sWorld->getIntConfig(CONFIG_SESSION_ADD_DELAY); + // ACE_OS::sleep(ACE_Time_Value(0, sleepTime)); + + sWorld->AddSession(_worldSession); + + return 0; +} + +void WorldTcpSession::SendAuthResponseError(uint8 code) +{ + WorldPacket packet(SMSG_AUTH_RESPONSE, 1); + packet << uint8(code); + + AsyncWrite(packet); +} diff --git a/src/server/game/Server/WorldTcpSession.h b/src/server/game/Server/WorldTcpSession.h new file mode 100644 index 00000000000..fde37a642e5 --- /dev/null +++ b/src/server/game/Server/WorldTcpSession.h @@ -0,0 +1,81 @@ +/* +* Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> +* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __WORLDTCPSESSION_H__ +#define __WORLDTCPSESSION_H__ + +#include <memory> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/streambuf.hpp> +#include "Common.h" +#include "AuthCrypt.h" +#include "Util.h" +#include "WorldPacket.h" +#include "WorldSession.h" + +using boost::asio::ip::tcp; + +#pragma pack(push, 1) + +struct ClientPktHeader +{ + uint16 size; + uint32 cmd; +}; + +#pragma pack(pop) + +class WorldTcpSession : public std::enable_shared_from_this<WorldTcpSession> +{ +public: + WorldTcpSession(tcp::socket socket) : _socket(std::move(socket)), _authSeed(static_cast<uint32> (rand32())) + { + + } + + void Start(); + + const std::string GetRemoteIpAddress() const { return _socket.remote_endpoint().address().to_string(); }; + unsigned short GetRemotePort() const { return _socket.remote_endpoint().port(); } + + void CloseSocket() { _socket.close(); }; + bool IsOpen() { return _socket.is_open(); }; + + void WorldTcpSession::AsyncWrite(WorldPacket const& packet); + +private: + + void WorldTcpSession::HandleSendAuthSession(); + int WorldTcpSession::HandleAuthSession(WorldPacket& recvPacket); + void WorldTcpSession::SendAuthResponseError(uint8 code); + + void WorldTcpSession::AsyncReadHeader(); + void WorldTcpSession::AsyncReadData(size_t dataSize); + + tcp::socket _socket; + + char _readBuffer[4096]; + char _writeBuffer[4096]; + + uint32 _authSeed; + AuthCrypt _authCrypt; + + WorldSession* _worldSession; +}; + +#endif diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 0fc2c1a7221..7ac60c631b3 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -37,12 +37,12 @@ #include "MapManager.h" #include "ObjectAccessor.h" #include "ScriptMgr.h" -#include "WorldSocketMgr.h" #include "OutdoorPvP/OutdoorPvPMgr.h" #include "BattlegroundMgr.h" #include "TCSoap.h" #include "CliRunnable.h" #include "SystemConfig.h" +#include "WorldTcpSession.h" #define TRINITY_CORE_CONFIG "worldserver.conf" #define WORLD_SLEEP_CONST 50 @@ -155,8 +155,6 @@ extern int main(int argc, char** argv) TC_LOG_INFO("server.worldserver", "Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100); OpenSSLCrypto::threadsSetup(); - BigNumber seed1; - seed1.SetRand(16 * 8); /// worldserver PID file creation std::string pidFile = sConfigMgr->GetStringDefault("PidFile", ""); @@ -228,21 +226,17 @@ extern int main(int argc, char** argv) // Launch the worldserver listener socket uint16 worldPort = uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD)); - std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); + std::string worldListener = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); - if (sWorldSocketMgr->StartNetwork(worldPort, bindIp.c_str()) == -1) - { - TC_LOG_ERROR("server.worldserver", "Failed to start network"); - return ERROR_EXIT_CODE; - } + AsyncAcceptor<WorldTcpSession> worldAcceptor(_ioService, worldListener, worldPort); + + sScriptMgr->OnStartup(); // Set server online (allow connecting now) LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_INVALID, realmID); TC_LOG_INFO("server.worldserver", "%s (worldserver-daemon) ready...", _FULLVERSION); - sScriptMgr->OnStartup(); - WorldUpdateLoop(); // Shutdown starts here @@ -262,8 +256,6 @@ extern int main(int argc, char** argv) // unload battleground templates before different singletons destroyed sBattlegroundMgr->DeleteAllBattlegrounds(); - sWorldSocketMgr->StopNetwork(); - sMapMgr->UnloadAll(); // unload all grids (including locked in memory) sObjectAccessor->UnloadAll(); // unload 'i_player2corpse' storage and remove from world sScriptMgr->Unload(); |