aboutsummaryrefslogtreecommitdiff
path: root/src/game/WorldSocket.cpp
diff options
context:
space:
mode:
authorNeo2003 <none@none>2008-10-04 06:17:19 -0500
committerNeo2003 <none@none>2008-10-04 06:17:19 -0500
commit5651828bf74edb760d67700942fc65d51c816e0a (patch)
tree1631f319bbc293794109f990beaccfd5b6cee8fe /src/game/WorldSocket.cpp
parentca7a4bf1a78a1ddc6eb21238cc2a1633194a11cc (diff)
[svn] * Added ACE for Linux and Windows (Thanks Derex for Linux part and partial Windows part)
* Updated to 6721 and 676 * Fixed TrinityScript logo * Version updated to 0.2.6721.676 --HG-- branch : trunk rename : 6700-670 => 6721-676
Diffstat (limited to 'src/game/WorldSocket.cpp')
-rw-r--r--src/game/WorldSocket.cpp1369
1 files changed, 897 insertions, 472 deletions
diff --git a/src/game/WorldSocket.cpp b/src/game/WorldSocket.cpp
index d52c0962862..cb1c0233b4d 100644
--- a/src/game/WorldSocket.cpp
+++ b/src/game/WorldSocket.cpp
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
*
* This program is free software; you can redistribute it and/or modify
@@ -16,649 +16,1074 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-/** \file
- \ingroup u2w
-*/
-
#include "Common.h"
-#include "Log.h"
+#include "WorldSocket.h"
+#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 "Util.h"
+#include "World.h"
+#include "WorldPacket.h"
+#include "SharedDefines.h"
+#include "ByteBuffer.h"
+#include "AddonHandler.h"
#include "Opcodes.h"
#include "Database/DatabaseEnv.h"
#include "Auth/Sha1.h"
-#include "WorldPacket.h"
-#include "WorldSocket.h"
#include "WorldSession.h"
-#include "World.h"
#include "WorldSocketMgr.h"
-#include "Policies/SingletonImp.h"
+#include "Log.h"
#include "WorldLog.h"
-#include "AddonHandler.h"
-#include "sockets/Utility.h"
-#include "Util.h"
-// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
#if defined( __GNUC__ )
#pragma pack(1)
#else
#pragma pack(push,1)
#endif
-/// Client Packet Header
-struct ClientPktHeader
+struct ServerPktHeader
{
- uint16 size;
- uint32 cmd;
+ ACE_UINT16 size;
+ ACE_UINT16 cmd;
};
-/// Server Packet Header
-struct ServerPktHeader
+struct ClientPktHeader
{
- uint16 size;
- uint16 cmd;
+ ACE_UINT16 size;
+ ACE_UINT32 cmd;
};
-// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
#if defined( __GNUC__ )
#pragma pack()
#else
#pragma pack(pop)
#endif
-#define SOCKET_CHECK_PACKET_SIZE(P,S) if((P).size() < (S)) return SizeError((P),(S));
-
-/// WorldSocket construction and initialization.
-WorldSocket::WorldSocket(ISocketHandler &sh): TcpSocket(sh), _cmd(0), _remaining(0), _session(NULL)
+// used when testing to alow login without password and encryption
+// #define _NETCODE_FAKE_AUTH
+
+WorldSocket::WorldSocket (void) :
+WorldHandler (),
+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 ())),
+m_OverSpeedPings (0),
+m_LastPingTime (ACE_Time_Value::zero)
{
- _seed = static_cast<uint32>(rand32());
- m_LastPingMSTime = 0; // first time it will counted as overspeed maybe, but this is not important
- m_OverSpeedPings = 0;
-
- if (sWorld.getConfig(CONFIG_TCP_NO_DELAY))
- SetTcpNodelay(true);
+ this->reference_counting_policy ().value (ACE_Event_Handler::Reference_Counting_Policy::ENABLED);
}
-/// WorldSocket destructor
-WorldSocket::~WorldSocket()
+WorldSocket::~WorldSocket (void)
{
- if(_session)
- _session->SetSocket(0);
+ if (m_RecvWPct)
+ delete m_RecvWPct;
- WorldPacket *packet;
+ if (m_OutBuffer)
+ m_OutBuffer->release ();
- ///- Go through the to-be-sent queue and delete remaining packets
- while(!_sendQueue.empty())
- {
- packet = _sendQueue.next();
- delete packet;
- }
+ this->closing_ = true;
+
+ this->peer ().close ();
+
+ WorldPacket* pct;
+ while (m_PacketQueue.dequeue_head (pct) == 0)
+ delete pct;
}
-/// Copy the packet to the to-be-sent queue
-void WorldSocket::SendPacket(WorldPacket const* packet)
+bool
+WorldSocket::IsClosed (void) const
{
- WorldPacket *pck = new WorldPacket(*packet);
- ASSERT(pck);
- _sendQueue.add(pck);
+ return this->closing_;
}
-/// On client connection
-void WorldSocket::OnAccept()
+void
+WorldSocket::CloseSocket (void)
{
- ///- Add the current socket to the list of sockets to be managed (WorldSocketMgr)
- sWorldSocketMgr.AddSocket(this);
- Utility::ResolveLocal();
+ {
+ ACE_GUARD (LockType, Guard, m_OutBufferLock);
+
+ if (this->closing_)
+ return;
+
+ this->closing_ = true;
+
+ this->peer ().close_writer ();
+ }
- ///- Send a AUTH_CHALLENGE packet
- WorldPacket packet( SMSG_AUTH_CHALLENGE, 4 );
- packet << _seed;
+ {
+ ACE_GUARD (LockType, Guard, m_SessionLock);
+
+ m_Session = NULL;
+ }
- SendPacket(&packet);
}
-/// Read the client transmitted data
-void WorldSocket::OnRead()
+const std::string&
+WorldSocket::GetRemoteAddress (void) const
{
- TcpSocket::OnRead();
+ return m_Address;
+}
- while(1)
- {
- ///- Read the packet header and decipher it (if needed)
- if (!_remaining)
- {
- if (ibuf.GetLength() < 6)
- break;
+int
+WorldSocket::SendPacket (const WorldPacket& pct)
+{
+ ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1);
- ClientPktHeader hdr;
+ if (this->closing_)
+ return -1;
- ibuf.Read((char *)&hdr, 6);
- _crypt.DecryptRecv((uint8 *)&hdr, 6);
+ // Dump outgoing packet.
+ if (sWorldLog.LogWorld ())
+ {
+ sWorldLog.Log ("SERVER:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n",
+ (uint32) get_handle (),
+ pct.size (),
+ LookupOpcodeName (pct.GetOpcode ()),
+ pct.GetOpcode ());
+
+ uint32 p = 0;
+ while (p < pct.size ())
+ {
+ for (uint32 j = 0; j < 16 && p < pct.size (); j++)
+ sWorldLog.Log ("%.2X ", const_cast<WorldPacket&>(pct)[p++]);
- _remaining = ntohs(hdr.size) - 4;
- _cmd = hdr.cmd;
+ sWorldLog.Log ("\n");
}
- if (ibuf.GetLength() < _remaining)
- break;
-
- ///- Read the remaining of the packet
- WorldPacket packet((uint16)_cmd, _remaining);
+ sWorldLog.Log ("\n\n");
+ }
- packet.resize(_remaining);
- if(_remaining) ibuf.Read((char*)packet.contents(), _remaining);
- _remaining = 0;
+ if (iSendPacket (pct) == -1)
+ {
+ WorldPacket* npct;
- ///- If log of world packets is enable, log the incoming packet
- if( sWorldLog.LogWorld() )
- {
- sWorldLog.Log("CLIENT:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n",
- (uint32)GetSocket(),
- packet.size(),
- LookupOpcodeName(packet.GetOpcode()),
- packet.GetOpcode());
-
- uint32 p = 0;
- while (p < packet.size())
- {
- for (uint32 j = 0; j < 16 && p < packet.size(); j++)
- sWorldLog.Log("%.2X ", packet[p++]);
- sWorldLog.Log("\n");
- }
- sWorldLog.Log("\n\n");
- }
+ ACE_NEW_RETURN (npct, WorldPacket (pct), -1);
- ///- If the packet is PING, KEEP_ALIVE or AUTH_SESSION, handle immediately
- switch (_cmd)
+ // NOTE maybe check of the size of the queue can be good ?
+ // to make it bounded instead of unbounded
+ if (m_PacketQueue.enqueue_tail (npct) == -1)
{
- case CMSG_KEEP_ALIVE:
- break; // just ignore, network connectivity timeout preventing
- case CMSG_PING:
- {
- _HandlePing(packet);
- break;
- }
- case CMSG_AUTH_SESSION:
- {
- _HandleAuthSession(packet);
- break;
- }
- default:
- {
- ///- Else, put it in the world session queue for this user (need to be already authenticated)
- if (_session)
- _session->QueuePacket(packet);
- else
- sLog.outDetail("Received out of place packet with cmdid 0x%.4X", _cmd);
- break;
- }
+ delete npct;
+ sLog.outError ("WorldSocket::SendPacket: m_PacketQueue.enqueue_tail failed");
+ return -1;
}
}
+
+ return 0;
}
-/// On socket closing
-void WorldSocket::CloseSocket()
+long
+WorldSocket::AddReference (void)
{
- ///- Set CloseAndDelete flag for TcpSocket class
- SetCloseAndDelete(true);
+ return static_cast<long> (this->add_reference ());
+}
- ///- Set _session to NULL. Prevent crashes
- _session = NULL;
+long
+WorldSocket::RemoveReference (void)
+{
+ return static_cast<long> (this->remove_reference ());
}
-/// On socket deleting
-void WorldSocket::OnDelete()
+int
+WorldSocket::open (void *a)
{
- ///- Stop sending remaining data through this socket
- if (_session)
+ 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 (this->peer ().get_remote_addr (remote_addr) == -1)
{
- _session->SetSocket(NULL);
- // Session deleted from World session list at socket==0, This is only back reference from socket to session.
- _session = NULL;
+ sLog.outError ("WorldSocket::open: peer ().get_remote_addr errno = %s", ACE_OS::strerror (errno));
+ return -1;
}
- ///- Remove the socket from the WorldSocketMgr list
- sWorldSocketMgr.RemoveSocket(this);
+ m_Address = remote_addr.get_host_addr ();
- ///- Removes socket from player queue
- sWorld.RemoveQueuedPlayer(this);
-}
+ // Send startup packet.
+ WorldPacket packet (SMSG_AUTH_CHALLENGE, 4);
+ packet << m_Seed;
-/// Handle the client authentication packet
-void WorldSocket::_HandleAuthSession(WorldPacket& recvPacket)
-{
- uint8 digest[20];
- uint32 clientSeed;
- uint32 unk2;
- uint32 BuiltNumberClient;
- uint32 id, security;
- bool tbc = false;
- std::string account;
- Sha1Hash sha1;
- BigNumber v, s, g, N, x, I;
- WorldPacket packet, SendAddonPacked;
-
- BigNumber K;
-
- SOCKET_CHECK_PACKET_SIZE(recvPacket,4+4+1+4+20);
-
- ///- Read the content of the packet
- recvPacket >> BuiltNumberClient; // for now no use
- recvPacket >> unk2;
- recvPacket >> account;
-
- // recheck size
- SOCKET_CHECK_PACKET_SIZE(recvPacket,4+4+(account.size()+1)+4+20);
-
- recvPacket >> clientSeed;
- recvPacket.read(digest, 20);
-
- sLog.outDebug("Auth: client %u, unk2 %u, account %s, clientseed %u", BuiltNumberClient, unk2, account.c_str(), clientSeed);
-
- ///- Normalize account name
- //utf8ToUpperOnlyLatin(account); -- client already send account in expected form
-
- ///- Get the account information from the realmd database
- std::string safe_account = account; // Duplicate, else will screw the SHA hash verification below
- loginDatabase.escape_string(safe_account);
- //No SQL injection, username escaped.
- // 0 1 2 3 4 5 6 7 8 9 10
- QueryResult *result = loginDatabase.PQuery("SELECT id, gmlevel, sessionkey, last_ip, locked, sha_pass_hash, v, s, tbc, mutetime, locale FROM account WHERE username = '%s'", safe_account.c_str());
-
- ///- Stop if the account is not found
- if ( !result )
+ if (SendPacket (packet) == -1)
+ return -1;
+
+ // Register with ACE Reactor
+ if (this->reactor ()->register_handler
+ (this,
+ ACE_Event_Handler::READ_MASK | ACE_Event_Handler::WRITE_MASK) == -1)
{
- packet.Initialize( SMSG_AUTH_RESPONSE, 1 );
- packet << uint8( AUTH_UNKNOWN_ACCOUNT );
- SendPacket( &packet );
- sLog.outDetail( "SOCKET: Sent Auth Response (unknown account)." );
- return;
+ sLog.outError ("WorldSocket::open: unable to register client handler errno = %s", ACE_OS::strerror (errno));
+ return -1;
}
- Field* fields = result->Fetch();
+ // reactor takes care of the socket from now on
+ this->remove_reference ();
- tbc = fields[8].GetUInt8() && sWorld.getConfig(CONFIG_EXPANSION) > 0;
+ return 0;
+}
- N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
- g.SetDword(7);
- I.SetHexStr(fields[5].GetString());
+int
+WorldSocket::close (int)
+{
+ this->shutdown ();
- //In case of leading zeros in the I hash, restore them
- uint8 mDigest[SHA_DIGEST_LENGTH];
- memset(mDigest,0,SHA_DIGEST_LENGTH);
- if (I.GetNumBytes() <= SHA_DIGEST_LENGTH)
- memcpy(mDigest,I.AsByteArray(),I.GetNumBytes());
+ this->closing_ = true;
- std::reverse(mDigest,mDigest+SHA_DIGEST_LENGTH);
+ this->remove_reference ();
- s.SetHexStr(fields[7].GetString());
- sha1.UpdateData(s.AsByteArray(), s.GetNumBytes());
- sha1.UpdateData(mDigest, SHA_DIGEST_LENGTH);
- sha1.Finalize();
- x.SetBinary(sha1.GetDigest(), sha1.GetLength());
- v = g.ModExp(x, N);
+ return 0;
+}
- const char* sStr = s.AsHexStr(); //Must be freed by OPENSSL_free()
- const char* vStr = v.AsHexStr(); //Must be freed by OPENSSL_free()
- const char* vold = fields[6].GetString();
- sLog.outDebug("SOCKET: (s,v) check s: %s v_old: %s v_new: %s", sStr, vold, vStr );
- loginDatabase.PExecute("UPDATE account SET v = '0', s = '0' WHERE username = '%s'", safe_account.c_str());
- if ( !vold || strcmp( vStr, vold ) )
- {
- packet.Initialize( SMSG_AUTH_RESPONSE, 1 );
- packet << uint8( AUTH_UNKNOWN_ACCOUNT );
- SendPacket( &packet );
- sLog.outDetail( "SOCKET: User not logged.");
- delete result;
- OPENSSL_free((void*)sStr);
- OPENSSL_free((void*)vStr);
- return;
- }
- OPENSSL_free((void*)sStr);
- OPENSSL_free((void*)vStr);
+int
+WorldSocket::handle_input (ACE_HANDLE)
+{
+ if (this->closing_)
+ return -1;
- ///- Re-check ip locking (same check as in realmd).
- if(fields[4].GetUInt8() == 1) // if ip is locked
+ switch (this->handle_input_missing_data ())
{
- if ( strcmp(fields[3].GetString(),GetRemoteAddress().c_str()) )
- {
- packet.Initialize( SMSG_AUTH_RESPONSE, 1 );
- packet << uint8( AUTH_FAILED );
- SendPacket( &packet );
-
- sLog.outDetail( "SOCKET: Sent Auth Response (Account IP differs)." );
- delete result;
- return;
- }
+ case -1 :
+ {
+ if ((errno == EWOULDBLOCK) ||
+ (errno == EAGAIN))
+ {
+ //return 0;
+ return this->Update (); // interesting line ,isnt it ?
+ }
+
+ DEBUG_LOG ("WorldSocket::handle_input: Peer error closing connection errno = %s", ACE_OS::strerror (errno));
+
+ return -1;
+ }
+ case 0:
+ {
+ DEBUG_LOG ("WorldSocket::handle_input: Peer has closed connection\n");
+
+ errno = ECONNRESET;
+
+ return -1;
+ }
+ case 1:
+ return 1;
}
- id = fields[0].GetUInt32();
- security = fields[1].GetUInt16();
- K.SetHexStr(fields[2].GetString());
- time_t mutetime = time_t(fields[9].GetUInt64());
+ //return 0;
+ return this->Update (); // another interesting line ;)
+}
+
+int
+WorldSocket::handle_output (ACE_HANDLE)
+{
+ ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1);
- LocaleConstant locale = LocaleConstant(fields[10].GetUInt8());
- if (locale>=MAX_LOCALE)
- locale=LOCALE_enUS;
+ if (this->closing_)
+ return -1;
- delete result;
+ const size_t send_len = m_OutBuffer->length ();
- ///- Re-check account ban (same check as in realmd) /// TO DO: why on earth do 2 checks for same thing?
- QueryResult *banresult = loginDatabase.PQuery("SELECT bandate,unbandate FROM account_banned WHERE id = '%u' AND active = 1", id);
- if(banresult) // if account banned
+ if (send_len == 0)
+ return this->cancel_wakeup_output (Guard);
+
+// TODO SO_NOSIGPIPE on platforms that support it
+#ifdef MSG_NOSIGNAL
+ ssize_t n = this->peer ().send (m_OutBuffer->rd_ptr (), send_len, MSG_NOSIGNAL);
+#else
+ ssize_t n = this->peer ().send (m_OutBuffer->rd_ptr (), send_len);
+#endif // MSG_NOSIGNAL
+
+ if (n == 0)
+ return -1;
+ else if (n == -1)
{
- packet.Initialize( SMSG_AUTH_RESPONSE, 1 );
- packet << uint8( AUTH_BANNED );
- SendPacket( &packet );
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ return this->schedule_wakeup_output (Guard);
- sLog.outDetail( "SOCKET: Sent Auth Response (Account banned)." );
- delete banresult;
- return;
+ return -1;
}
-
- ///- Check locked state for server
- AccountTypes allowedAccountType = sWorld.GetPlayerSecurityLimit();
- if( allowedAccountType > SEC_PLAYER && security < allowedAccountType)
+ else if (n < send_len) //now n > 0
{
- WorldPacket Packet(SMSG_AUTH_RESPONSE, 1);
- Packet << uint8(AUTH_UNAVAILABLE);
- SendPacket(&Packet);
- return;
- }
+ m_OutBuffer->rd_ptr (static_cast<size_t> (n));
+
+ // move the data to the base of the buffer
+ m_OutBuffer->crunch ();
- ///- kick already loaded player with same account (if any) and remove session
- ///- if player is in loading and want to load again, return
- if(!sWorld.RemoveSession(id))
+ return this->schedule_wakeup_output (Guard);
+ }
+ else //now n == send_len
{
- return;
+ m_OutBuffer->reset ();
+
+ if (!iFlushPacketQueue ())
+ return this->cancel_wakeup_output (Guard);
+ else
+ return this->schedule_wakeup_output (Guard);
}
- ///- Check that Key and account name are the same on client and server
- Sha1Hash sha;
+ ACE_NOTREACHED (return 0);
+}
- uint32 t = 0;
- uint32 seed = _seed;
+int
+WorldSocket::handle_close (ACE_HANDLE h, ACE_Reactor_Mask)
+{
+ // Critical section
+ {
+ ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1);
- sha.UpdateData(account);
- sha.UpdateData((uint8 *)&t, 4);
- sha.UpdateData((uint8 *)&clientSeed, 4);
- sha.UpdateData((uint8 *)&seed, 4);
- sha.UpdateBigNumbers(&K, NULL);
- sha.Finalize();
+ this->closing_ = true;
- if (memcmp(sha.GetDigest(), digest, 20))
- {
- packet.Initialize( SMSG_AUTH_RESPONSE, 1 );
- packet << uint8( AUTH_FAILED );
- SendPacket( &packet );
+ if (h == ACE_INVALID_HANDLE)
+ this->peer ().close_writer ();
+ }
- sLog.outDetail( "SOCKET: Sent Auth Response (authentication failed)." );
- return;
- }
+ // Critical section
+ {
+ ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1);
- ///- Initialize the encryption with the Key
- _crypt.SetKey(&K);
- _crypt.Init();
+ m_Session = NULL;
+ }
- ///- Send 'Auth is ok'
- packet.Initialize( SMSG_AUTH_RESPONSE, 1+4+1+4+1 );
- packet << uint8( AUTH_OK );
- packet << uint32(0); // unknown random value...
- packet << uint8(0); // can be 0 and 2
- packet << uint32(0); // const 0
- packet << uint8(tbc ? 1 : 0); // 0 - normal, 1 - TBC, must be set in database manually for each account
- SendPacket(&packet);
+ return 0;
+}
- ///- Create a new WorldSession for the player and add it to the World
- _session = new WorldSession(id, this,security,tbc,mutetime,locale);
- sWorld.AddSession(_session);
+int
+WorldSocket::Update (void)
+{
+ if (this->closing_)
+ return -1;
- if(sLog.IsOutDebug()) // optimize disabled debug output
- {
- sLog.outDebug( "SOCKET: Client '%s' authenticated successfully.", account.c_str() );
- sLog.outDebug( "Account: '%s' Logged in from IP %s.", account.c_str(), GetRemoteAddress().c_str());
- }
+ if (m_OutActive || m_OutBuffer->length () == 0)
+ return 0;
- ///- Update the last_ip in the database
- //No SQL injection, username escaped.
- std::string address = GetRemoteAddress();
- loginDatabase.escape_string(address);
- loginDatabase.PExecute("UPDATE account SET last_ip = '%s' WHERE username = '%s'",address.c_str(), safe_account.c_str());
-
- // do small delay (10ms) at accepting successful authed connection to prevent dropping packets by client
- // don't must harm anyone (let login ~100 accounts in 1 sec ;) )
- #ifdef WIN32
- Sleep(10);
- #else
- ZThread::Thread::sleep(10);
- #endif
-
- ///- Check that we do not exceed the maximum number of online players in the realm
- uint32 Sessions = sWorld.GetActiveAndQueuedSessionCount();
- uint32 pLimit = sWorld.GetPlayerAmountLimit();
- uint32 QueueSize = sWorld.GetQueueSize(); //number of players in the queue
- bool inQueue = false;
- --Sessions; //so we don't count the user trying to login as a session and queue the socket that we are using
-
- if( pLimit > 0 && Sessions >= pLimit && security == SEC_PLAYER )
+ return this->handle_output (this->get_handle ());
+}
+
+int
+WorldSocket::handle_input_header (void)
+{
+ ACE_ASSERT (m_RecvWPct == NULL);
+
+ if (m_Header.length () != sizeof (ClientPktHeader))
{
- sWorld.AddQueuedPlayer(this);
- SendAuthWaitQue(sWorld.GetQueuePos(this));
- sWorld.UpdateMaxSessionCounters();
- sLog.outDetail( "PlayerQueue: %s is in Queue Position (%u).",safe_account.c_str(),++QueueSize);
- inQueue = true;
+ sLog.outError ("WorldSocket::handle_input_header: internal error: invalid header");
+ errno = EINVAL;
+ return -1;
}
- ///- Create and send the Addon packet
- if(sAddOnHandler.BuildAddonPacket(&recvPacket, &SendAddonPacked))
- SendPacket(&SendAddonPacked);
+ m_Crypt.DecryptRecv ((ACE_UINT8*) m_Header.rd_ptr (), sizeof (ClientPktHeader));
- if(inQueue)
- return;
+ ClientPktHeader& header = *((ClientPktHeader*) m_Header.rd_ptr ());
- sWorld.UpdateMaxSessionCounters();
+ header.size = ACE_NTOHS (header.size);
- // Updates the population
- if (pLimit > 0)
+#if ACE_BYTE_ORDER == ACE_BIG_ENDIAN
+ header.cmd = ACE_SWAP_LONG (header.cmd)
+#endif // ACE_BIG_ENDIAN
+
+ if ((header.size < 4) ||
+ (header.size > 10240) ||
+ (header.cmd <= 0) ||
+ (header.cmd > 10240)
+ )
{
- float popu = sWorld.GetActiveSessionCount(); //updated number of users on the server
- popu /= pLimit;
- popu *= 2;
- loginDatabase.PExecute("UPDATE realmlist SET population = '%f' WHERE id = '%d'",popu,realmID);
- sLog.outDetail( "Server Population (%f).",popu);
+ sLog.outError ("WorldSocket::handle_input_header: client sent mailformed packet size = %d , cmd = %d",
+ header.size,
+ header.cmd);
+
+ errno = EINVAL;
+ return -1;
}
- return;
+ 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;
}
-/// Handle the Ping packet
-void WorldSocket::_HandlePing(WorldPacket& recvPacket)
+int
+WorldSocket::handle_input_payload (void)
{
- uint32 ping;
- uint32 latency;
+ // 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 = this->ProcessIncoming (m_RecvWPct);
- CHECK_PACKET_SIZE(recvPacket,8);
+ m_RecvPct.base (NULL, 0);
+ m_RecvPct.reset ();
+ m_RecvWPct = NULL;
- ///- Get the ping packet content
- recvPacket >> ping;
- recvPacket >> latency;
+ m_Header.reset ();
- if (_session )
- _session->SetLatency(latency);
+ if (ret == -1)
+ errno = EINVAL;
+
+ return ret;
+}
+
+int
+WorldSocket::handle_input_missing_data (void)
+{
+ char buf [1024];
- ///- check ping speed for players
- if(_session && _session->GetSecurity() == SEC_PLAYER)
+ 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 = this->peer ().recv (message_block.wr_ptr (),
+ recv_size);
+
+ if (n <= 0)
+ return n;
+
+ message_block.wr_ptr (n);
+
+ while (message_block.length () > 0)
{
- uint32 cur_mstime = getMSTime();
+ if (m_Header.space () > 0)
+ {
+ //need to recieve 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)
+ {
+ //couldnt recieve the whole header this time
+ ACE_ASSERT (message_block.length () == 0);
+ errno = EWOULDBLOCK;
+ return -1;
+ }
+
+ //we just recieved nice new header
+ if (this->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 recieves more chunked data and stuff
+ // hope this is not hack ,as proper m_RecvWPct is asserted around
+ if (!m_RecvWPct)
+ {
+ sLog.outError ("Forsing close on input m_RecvWPct = NULL");
+ errno = EINVAL;
+ return -1;
+ }
- // can overflow and start from 0
- uint32 diff_mstime = getMSTimeDiff(m_LastPingMSTime,cur_mstime);
- m_LastPingMSTime = cur_mstime;
- if(diff_mstime < 27000) // should be 30000 (=30 secs), add little tolerance
+ // We have full readed header, now check the data payload
+ if (m_RecvPct.space () > 0)
{
- ++m_OverSpeedPings;
+ //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);
- uint32 max_count = sWorld.getConfig(CONFIG_MAX_OVERSPEED_PINGS);
- if(max_count && m_OverSpeedPings > max_count)
+ if (m_RecvPct.space () > 0)
{
- sLog.outBasic("Player %s from account id %u kicked for overspeed ping packets from client (non-playable connection lags or cheating) ",_session->GetPlayerName(),_session->GetAccountId());
- _session->KickPlayer();
- return;
+ //couldnt recieve the whole data this time
+ ACE_ASSERT (message_block.length () == 0);
+ errno = EWOULDBLOCK;
+ return -1;
}
}
- else
- m_OverSpeedPings = 0;
+ //just recieved fresh new payload
+ if (this->handle_input_payload () == -1)
+ {
+ ACE_ASSERT ((errno != EWOULDBLOCK) && (errno != EAGAIN));
+ return -1;
+ }
}
- ///- And put the pong answer in the to-be-sent queue
- WorldPacket packet( SMSG_PONG, 4 );
- packet << ping;
- SendPacket(&packet);
+ return n == recv_size ? 1 : 2;
+}
+
+int
+WorldSocket::cancel_wakeup_output (GuardType& g)
+{
+ if (!m_OutActive)
+ return 0;
+
+ m_OutActive = false;
+
+ g.release ();
- return;
+ if (this->reactor ()->cancel_wakeup
+ (this, ACE_Event_Handler::WRITE_MASK) == -1)
+ {
+ // would be good to store errno from reactor with errno guard
+ sLog.outError ("WorldSocket::cancel_wakeup_output");
+ return -1;
+ }
+
+ return 0;
}
-/// Handle the update order for the socket
-void WorldSocket::SendSinglePacket()
+int
+WorldSocket::schedule_wakeup_output (GuardType& g)
{
- WorldPacket *packet;
- ServerPktHeader hdr;
+ if (m_OutActive)
+ return 0;
+
+ m_OutActive = true;
- ///- If we have packet to send
- if (!_sendQueue.empty())
+ g.release ();
+
+ if (this->reactor ()->schedule_wakeup
+ (this, ACE_Event_Handler::WRITE_MASK) == -1)
{
- packet = _sendQueue.next();
+ sLog.outError ("WorldSocket::schedule_wakeup_output");
+ return -1;
+ }
- hdr.size = ntohs((uint16)packet->size() + 2);
- hdr.cmd = packet->GetOpcode();
+ return 0;
+}
- if( sWorldLog.LogWorld() )
- {
- sWorldLog.Log("SERVER:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n",
- (uint32)GetSocket(),
- packet->size(),
- LookupOpcodeName(packet->GetOpcode()),
- packet->GetOpcode());
-
- uint32 p = 0;
- while (p < packet->size())
- {
- for (uint32 j = 0; j < 16 && p < packet->size(); j++)
- sWorldLog.Log("%.2X ", (*packet)[p++]);
+int
+WorldSocket::ProcessIncoming (WorldPacket* new_pct)
+{
+ ACE_ASSERT (new_pct);
+
+ // manage memory ;)
+ ACE_Auto_Ptr<WorldPacket> aptr (new_pct);
- sWorldLog.Log("\n");
- }
+ const ACE_UINT16 opcode = new_pct->GetOpcode ();
- sWorldLog.Log("\n\n");
+ if (this->closing_)
+ return -1;
+
+ // dump recieved packet
+ if (sWorldLog.LogWorld ())
+ {
+ sWorldLog.Log ("CLIENT:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n",
+ (uint32) get_handle (),
+ new_pct->size (),
+ LookupOpcodeName (new_pct->GetOpcode ()),
+ new_pct->GetOpcode ());
+
+ uint32 p = 0;
+ while (p < new_pct->size ())
+ {
+ for (uint32 j = 0; j < 16 && p < new_pct->size (); j++)
+ sWorldLog.Log ("%.2X ", (*new_pct)[p++]);
+ sWorldLog.Log ("\n");
}
+ sWorldLog.Log ("\n\n");
+ }
- ///- Encrypt (if needed) the header
- _crypt.EncryptSend((uint8*)&hdr, 4);
+ // like one switch ;)
+ if (opcode == CMSG_PING)
+ {
+ return HandlePing (*new_pct);
+ }
+ else if (opcode == CMSG_AUTH_SESSION)
+ {
+ if (m_Session)
+ {
+ sLog.outError ("WorldSocket::ProcessIncoming: Player send CMSG_AUTH_SESSION again");
+ return -1;
+ }
- ///- Send the header and body to the client
- TcpSocket::SendBuf((char*)&hdr, 4);
- if(!packet->empty()) TcpSocket::SendBuf((char*)packet->contents(), packet->size());
+ return HandleAuthSession (*new_pct);
+ }
+ else if (opcode == CMSG_KEEP_ALIVE)
+ {
+ DEBUG_LOG ("CMSG_KEEP_ALIVE ,size: %d", new_pct->size ());
- delete packet;
+ return 0;
}
+ else
+ {
+ ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1);
+
+ if (m_Session != NULL)
+ {
+ // OK ,give the packet to WorldSession
+ aptr.release ();
+ // WARNINIG here we call it with locks held.
+ // Its possible to cause deadlock if QueuePacket calls back
+ m_Session->QueuePacket (new_pct);
+ return 0;
+ }
+ else
+ {
+ sLog.outError ("WorldSocket::ProcessIncoming: Client not authed opcode = ", opcode);
+ return -1;
+ }
+ }
+
+ ACE_NOTREACHED (return 0);
}
-void WorldSocket::Update(time_t diff)
+int
+WorldSocket::HandleAuthSession (WorldPacket& recvPacket)
{
- const uint32 SEND_PACKETS_MAX = 100;
- const uint32 SEND_BUFFER_SIZE = 1024;
+ uint8 digest[20];
+ uint32 clientSeed;
+ uint32 unk2;
+ uint32 BuiltNumberClient;
+ uint32 id, security;
+ bool tbc = false;
+ LocaleConstant locale;
+ std::string account;
+ Sha1Hash sha1;
+ BigNumber v, s, g, N, x, I;
+ WorldPacket packet, SendAddonPacked;
+
+ BigNumber K;
+
+ if (recvPacket.size () < (4 + 4 + 1 + 4 + 20))
+ {
+ sLog.outError ("WorldSocket::HandleAuthSession: wrong packet size");
+ return -1;
+ }
+
+ // Read the content of the packet
+ recvPacket >> BuiltNumberClient; // for now no use
+ recvPacket >> unk2;
+ recvPacket >> account;
- uint8 sendBuffer[SEND_BUFFER_SIZE];
+ if (recvPacket.size () < (4 + 4 + (account.size () + 1) + 4 + 20))
+ {
+ sLog.outError ("WorldSocket::HandleAuthSession: wrong packet size second check");
+ return -1;
+ }
+
+ recvPacket >> clientSeed;
+ recvPacket.read (digest, 20);
+
+ DEBUG_LOG ("WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, clientseed %u",
+ BuiltNumberClient,
+ unk2,
+ account.c_str (),
+ clientSeed);
- while (!_sendQueue.empty())
+#if defined _NETCODE_FAKE_AUTH
+ bool dontchechtheacc = false;
+ uint8 digest_fake[sizeof (digest)];
+ memset ((void*) digest_fake, '\0', sizeof (digest_fake));
+ if (memcmp ((void*) digest, (void*) digest_fake, sizeof (digest_fake)) == 0)
{
- bool haveBigPacket = false;
- uint32 bufferSize = 0;
+ dontchechtheacc = true;
+ }
+#endif //_NETCODE_FAKE_AUTH
+
+ // Get the account information from the realmd database
+ std::string safe_account = account; // Duplicate, else will screw the SHA hash verification below
+ loginDatabase.escape_string (safe_account);
+ // No SQL injection, username escaped.
+
+ QueryResult *result =
+ loginDatabase.PQuery ("SELECT "
+ "id, " //0
+ "gmlevel, " //1
+ "sessionkey, " //2
+ "last_ip, " //3
+ "locked, " //4
+ "sha_pass_hash, " //5
+ "v, " //6
+ "s, " //7
+ "tbc, " //8
+ "mutetime, " //9
+ "locale " //10
+ "FROM account "
+ "WHERE username = '%s'",
+ safe_account.c_str ());
+
+ // Stop if the account is not found
+ if (!result)
+ {
+ packet.Initialize (SMSG_AUTH_RESPONSE, 1);
+ packet << uint8 (AUTH_UNKNOWN_ACCOUNT);
+
+ SendPacket (packet);
+
+ sLog.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (unknown account).");
+ return -1;
+ }
+
+ Field* fields = result->Fetch ();
+
+ tbc = fields[8].GetUInt8 () && sWorld.getConfig (CONFIG_EXPANSION) > 0;
+
+ N.SetHexStr ("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
+ g.SetDword (7);
+ I.SetHexStr (fields[5].GetString ());
+
+ //In case of leading zeros in the I hash, restore them
+ uint8 mDigest[SHA_DIGEST_LENGTH];
+ memset (mDigest, 0, SHA_DIGEST_LENGTH);
+
+ if (I.GetNumBytes () <= SHA_DIGEST_LENGTH)
+ memcpy (mDigest, I.AsByteArray (), I.GetNumBytes ());
+
+ std::reverse (mDigest, mDigest + SHA_DIGEST_LENGTH);
+
+ s.SetHexStr (fields[7].GetString ());
+ sha1.UpdateData (s.AsByteArray (), s.GetNumBytes ());
+ sha1.UpdateData (mDigest, SHA_DIGEST_LENGTH);
+ sha1.Finalize ();
+ x.SetBinary (sha1.GetDigest (), sha1.GetLength ());
+ v = g.ModExp (x, N);
+
+ const char* sStr = s.AsHexStr (); //Must be freed by OPENSSL_free()
+ const char* vStr = v.AsHexStr (); //Must be freed by OPENSSL_free()
+ const char* vold = fields[6].GetString ();
- ///- While we have packets to send
- for (uint32 packetCount = 0; (packetCount < SEND_PACKETS_MAX) && !_sendQueue.empty(); packetCount++)
+ DEBUG_LOG ("WorldSocket::HandleAuthSession: (s,v) check s: %s v_old: %s v_new: %s",
+ sStr,
+ vold,
+ vStr);
+
+ loginDatabase.PExecute ("UPDATE account "
+ "SET "
+ "v = '0', "
+ "s = '0' "
+ "WHERE username = '%s'",
+ safe_account.c_str ());
+
+#if defined _NETCODE_FAKE_AUTH
+ if (!dontchechtheacc)
+ {
+#endif
+ if (!vold || strcmp (vStr, vold))
{
- ServerPktHeader *hdr = (ServerPktHeader*)&sendBuffer[bufferSize];
+ packet.Initialize (SMSG_AUTH_RESPONSE, 1);
+ packet << uint8 (AUTH_UNKNOWN_ACCOUNT);
+ SendPacket (packet);
+ delete result;
+ OPENSSL_free ((void*) sStr);
+ OPENSSL_free ((void*) vStr);
+
+ sLog.outError ("WorldSocket::HandleAuthSession: User not logged.");
+ return -1;
+ }
+#if defined _NETCODE_FAKE_AUTH
+ }
+#endif
- // check merge possibility.
- WorldPacket *front = _sendQueue.front();
- uint32 packetSize = front->size();
+ OPENSSL_free ((void*) sStr);
+ OPENSSL_free ((void*) vStr);
- if ((sizeof(*hdr) + packetSize) > SEND_BUFFER_SIZE)
- {
- haveBigPacket = true;
- break;
- }
+ ///- Re-check ip locking (same check as in realmd).
+ if (fields[4].GetUInt8 () == 1) // if ip is locked
+ {
+ if (strcmp (fields[3].GetString (), GetRemoteAddress ().c_str ()))
+ {
+ packet.Initialize (SMSG_AUTH_RESPONSE, 1);
+ packet << uint8 (AUTH_FAILED);
+ SendPacket (packet);
- if ((bufferSize + sizeof(*hdr) + packetSize) > sizeof(sendBuffer))
- break;
+ delete result;
+ sLog.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs).");
+ return -1;
+ }
+ }
- // can be merged
- WorldPacket *packet = _sendQueue.next();
+ id = fields[0].GetUInt32 ();
+ security = fields[1].GetUInt16 ();
+ K.SetHexStr (fields[2].GetString ());
- hdr->size = ntohs((uint16)packetSize + 2);
- hdr->cmd = packet->GetOpcode();
+ time_t mutetime = time_t (fields[9].GetUInt64 ());
- if( sWorldLog.LogWorld() )
- {
- sWorldLog.Log("SERVER:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n",
- (uint32)GetSocket(),
- packetSize,
- LookupOpcodeName(packet->GetOpcode()),
- packet->GetOpcode());
-
- uint32 p = 0;
- while (p < packetSize)
- {
- for (uint32 j = 0; j < 16 && p < packetSize; j++)
- sWorldLog.Log("%.2X ", (*packet)[p++]);
+ locale = LocaleConstant (fields[10].GetUInt8 ());
+ if (locale >= MAX_LOCALE)
+ locale = LOCALE_enUS;
- sWorldLog.Log("\n");
- }
+ delete result;
- sWorldLog.Log("\n\n");
- }
+#if defined _NETCODE_FAKE_AUTH
+ if (!dontchechtheacc)
+ {
+#endif
+ // Re-check account ban (same check as in realmd)
+ QueryResult *banresult =
+ loginDatabase.PQuery ("SELECT "
+ "bandate, "
+ "unbandate "
+ "FROM account_banned "
+ "WHERE id = '%u' "
+ "AND active = 1",
+ id);
+
+ if (banresult) // if account banned
+ {
+ packet.Initialize (SMSG_AUTH_RESPONSE, 1);
+ packet << uint8 (AUTH_BANNED);
+ SendPacket (packet);
- ///- Encrypt (if needed) the header
- _crypt.EncryptSend((uint8*)hdr, sizeof(*hdr));
- bufferSize += sizeof(*hdr);
+ delete banresult;
- if (packetSize)
- {
- memcpy(&sendBuffer[bufferSize], packet->contents(), packetSize);
- bufferSize += packetSize;
- }
+ sLog.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (Account banned).");
+ return -1;
+ }
+
+ // Check locked state for server
+ AccountTypes allowedAccountType = sWorld.GetPlayerSecurityLimit ();
- ///- Send the header and body to the client
- delete packet;
+ if (allowedAccountType > SEC_PLAYER && security < allowedAccountType)
+ {
+ WorldPacket Packet (SMSG_AUTH_RESPONSE, 1);
+ Packet << uint8 (AUTH_UNAVAILABLE);
+
+ SendPacket (packet);
+
+ sLog.outBasic ("WorldSocket::HandleAuthSession: User tryes to login but his security level is not enough");
+ return -1;
}
- // send merged packets
- if (bufferSize) TcpSocket::SendBuf((char*)sendBuffer, bufferSize);
- // send too big non-merged packet
- if (haveBigPacket) SendSinglePacket();
+ // Check that Key and account name are the same on client and server
+ Sha1Hash sha;
+
+ 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))
+ {
+ packet.Initialize (SMSG_AUTH_RESPONSE, 1);
+ packet << uint8 (AUTH_FAILED);
+
+ SendPacket (packet);
+
+ sLog.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (authentification failed).");
+ return -1;
+ }
+#if defined _NETCODE_FAKE_AUTH
}
+#endif
+
+ std::string address = this->GetRemoteAddress ();
+
+ DEBUG_LOG ("WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.",
+ account.c_str (),
+ address.c_str ());
+
+ // Update the last_ip in the database
+ // No SQL injection, username escaped.
+ loginDatabase.escape_string (address);
+
+ loginDatabase.PExecute ("UPDATE account "
+ "SET last_ip = '%s' "
+ "WHERE username = '%s'",
+ address.c_str (),
+ safe_account.c_str ());
+
+ // TODO protect here probably ?
+ // Althought atm the socket is singlethreaded
+ ACE_NEW_RETURN (m_Session, WorldSession (id, this, security, tbc, mutetime, locale), -1);
+
+#if defined _NETCODE_FAKE_AUTH
+ if (!dontchechtheacc)
+ {
+#endif
+ this->m_Crypt.SetKey (&K);
+ this->m_Crypt.Init ();
+#if defined _NETCODE_FAKE_AUTH
+ }
+#endif
+
+ // In case needed sometime the second arg is in microseconds 1 000 000 = 1 sec
+ ACE_OS::sleep (ACE_Time_Value (0, 10000));
+
+ // TODO error handling
+ sWorld.AddSession (this->m_Session);
+
+ // Create and send the Addon packet
+ if (sAddOnHandler.BuildAddonPacket (&recvPacket, &SendAddonPacked))
+ SendPacket (SendAddonPacked);
+
+ return 0;
}
-/// Handle the authentication waiting queue (to be completed)
-void WorldSocket::SendAuthWaitQue(uint32 position)
+int
+WorldSocket::HandlePing (WorldPacket& recvPacket)
{
- if(position == 0)
+ uint32 ping;
+ uint32 latency;
+
+ if (recvPacket.size () < 8)
{
- WorldPacket packet( SMSG_AUTH_RESPONSE, 1 );
- packet << uint8( AUTH_OK );
- SendPacket(&packet);
+ sLog.outError ("WorldSocket::_HandlePing wrong packet size");
+ return -1;
}
+
+ // 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.getConfig (CONFIG_MAX_OVERSPEED_PINGS);
+
+ if (max_count && m_OverSpeedPings > max_count)
+ {
+ ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1);
+
+ if (m_Session && m_Session->GetSecurity () == SEC_PLAYER)
+ {
+ sLog.outError ("WorldSocket::HandlePing: Player kicked for "
+ "overspeeded pings adress = %s",
+ 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);
else
+ {
+ sLog.outError ("WorldSocket::HandlePing: peer sent CMSG_PING, "
+ "but is not authenticated or got recently kicked,"
+ " adress = %s",
+ this->GetRemoteAddress ().c_str ());
+ return -1;
+ }
+ }
+
+ WorldPacket packet (SMSG_PONG, 4);
+ packet << ping;
+ return this->SendPacket (packet);
+}
+
+int
+WorldSocket::iSendPacket (const WorldPacket& pct)
+{
+ if (m_OutBuffer->space () < pct.size () + sizeof (ServerPktHeader))
{
- WorldPacket packet( SMSG_AUTH_RESPONSE, 5 );
- packet << uint8( AUTH_WAIT_QUEUE );
- packet << uint32 (position); //amount of players in queue
- SendPacket(&packet);
+ errno = ENOBUFS;
+ return -1;
}
+
+ ServerPktHeader header;
+
+ header.cmd = pct.GetOpcode ();
+
+#if ACE_BYTE_ORDER == ACE_BIG_ENDIAN
+ header.cmd = ACE_SWAP_WORD (header.cmd)
+#endif
+
+ header.size = (uint16) pct.size () + 2;
+ header.size = ACE_HTONS (header.size);
+
+ m_Crypt.EncryptSend ((uint8*) & header, sizeof (header));
+
+ if (m_OutBuffer->copy ((char*) & header, sizeof (header)) == -1)
+ ACE_ASSERT (false);
+
+ if (!pct.empty ())
+ if (m_OutBuffer->copy ((char*) pct.contents (), pct.size ()) == -1)
+ ACE_ASSERT (false);
+
+ return 0;
}
-void WorldSocket::SizeError(WorldPacket const& packet, uint32 size) const
+bool
+WorldSocket::iFlushPacketQueue ()
{
- sLog.outError("Client send packet %s (%u) with size %u but expected %u (attempt crash server?), skipped",
- LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size);
+ WorldPacket *pct;
+ bool haveone = false;
+
+ while (m_PacketQueue.dequeue_head (pct) == 0)
+ {
+ if (iSendPacket (*pct) == -1)
+ {
+ if (m_PacketQueue.enqueue_head (pct) == -1)
+ {
+ delete pct;
+ sLog.outError ("WorldSocket::iFlushPacketQueue m_PacketQueue->enqueue_head");
+ return false;
+ }
+
+ break;
+ }
+ else
+ {
+ haveone = true;
+ delete pct;
+ }
+ }
+
+ return haveone;
}