Some groundwork for replacing the ACE based WorldSocket handling
This commit is contained in:
1 parent
021e18d152
commit
7befb26625
13 files changed
+579
-1819
No files matched your search
@@ -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);
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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 */
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -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_ */
|
||||
/// @}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
/// @}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
Reference in new issue
Block a user