aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorleak <leak@bitmx.net>2014-07-06 01:26:29 +0200
committerleak <leak@bitmx.net>2014-07-06 01:26:29 +0200
commit7befb26625dd1eeb237e223296d9ed8817297025 (patch)
tree3226885cc3d93e656bcd4e02ad4366bc77de5994 /src
parent021e18d152b68b9d2a0c5887bab7eb7b61ecd2ca (diff)
Some groundwork for replacing the ACE based WorldSocket handling
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp10
-rw-r--r--src/server/game/Scripting/ScriptMgr.h24
-rw-r--r--src/server/game/Server/Protocol/ServerPktHeader.h59
-rw-r--r--src/server/game/Server/WorldSession.cpp20
-rw-r--r--src/server/game/Server/WorldSession.h6
-rw-r--r--src/server/game/Server/WorldSocket.cpp1050
-rw-r--r--src/server/game/Server/WorldSocket.h218
-rw-r--r--src/server/game/Server/WorldSocketAcceptor.h67
-rw-r--r--src/server/game/Server/WorldSocketMgr.cpp360
-rw-r--r--src/server/game/Server/WorldSocketMgr.h78
-rw-r--r--src/server/game/Server/WorldTcpSession.cpp407
-rw-r--r--src/server/game/Server/WorldTcpSession.h81
-rw-r--r--src/server/worldserver/Main.cpp18
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();