diff options
Diffstat (limited to 'src/server/game/WorldSocket.cpp')
-rw-r--r-- | src/server/game/WorldSocket.cpp | 1064 |
1 files changed, 1064 insertions, 0 deletions
diff --git a/src/server/game/WorldSocket.cpp b/src/server/game/WorldSocket.cpp new file mode 100644 index 00000000000..c07b369d0b9 --- /dev/null +++ b/src/server/game/WorldSocket.cpp @@ -0,0 +1,1064 @@ +/* +* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> +* +* Copyright (C) 2008-2010 Trinity <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, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <ace/Message_Block.h> +#include <ace/OS_NS_string.h> +#include <ace/OS_NS_unistd.h> +#include <ace/os_include/arpa/os_inet.h> +#include <ace/os_include/netinet/os_tcp.h> +#include <ace/os_include/sys/os_types.h> +#include <ace/os_include/sys/os_socket.h> +#include <ace/OS_NS_string.h> +#include <ace/Reactor.h> +#include <ace/Auto_Ptr.h> + +#include "WorldSocket.h" +#include "Common.h" + +#include "Util.h" +#include "World.h" +#include "WorldPacket.h" +#include "SharedDefines.h" +#include "ByteBuffer.h" +#include "Opcodes.h" +#include "Database/DatabaseEnv.h" +#include "Auth/BigNumber.h" +#include "Auth/Sha1.h" +#include "WorldSession.h" +#include "WorldSocketMgr.h" +#include "Log.h" +#include "WorldLog.h" + +#if defined(__GNUC__) +#pragma pack(1) +#else +#pragma pack(push,1) +#endif + +struct ServerPktHeader +{ + /** + * size is the length of the payload _plus_ the length of the opcode + */ + ServerPktHeader(uint32 size, uint16 cmd) : size(size) + { + uint8 headerIndex=0; + if (isLargePacket()) + { + sLog.outDebug("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() + { + return size > 0x7FFF; + } + + const uint32 size; + uint8 header[5]; +}; + +struct ClientPktHeader +{ + uint16 size; + uint32 cmd; +}; + +#if defined(__GNUC__) +#pragma pack() +#else +#pragma pack(pop) +#endif + +WorldSocket::WorldSocket (void) : +WorldHandler(), +m_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) +{ + reference_counting_policy().value (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); + + msg_queue()->high_water_mark(8*1024*1024); + msg_queue()->low_water_mark(8*1024*1024); +} + +WorldSocket::~WorldSocket (void) +{ + if (m_RecvWPct) + delete m_RecvWPct; + + if (m_OutBuffer) + m_OutBuffer->release(); + + closing_ = true; + + peer().close(); +} + +bool WorldSocket::IsClosed (void) const +{ + return closing_; +} + +void WorldSocket::CloseSocket (void) +{ + { + ACE_GUARD (LockType, Guard, m_OutBufferLock); + + if (closing_) + return; + + closing_ = true; + peer().close_writer(); + } + + { + ACE_GUARD (LockType, Guard, m_SessionLock); + + m_Session = NULL; + } +} + +const std::string& WorldSocket::GetRemoteAddress (void) const +{ + return m_Address; +} + +int WorldSocket::SendPacket (const WorldPacket& pct) +{ + ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1); + + if (closing_) + return -1; + + // Dump outgoing packet. + if (sWorldLog.LogWorld()) + { + sWorldLog.outTimestampLog ("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.outLog("%.2X ", const_cast<WorldPacket&>(pct)[p++]); + + sWorldLog.outLog("\n"); + } + sWorldLog.outLog("\n"); + } + + ServerPktHeader header(pct.size()+2, pct.GetOpcode()); + m_Crypt.EncryptSend ((uint8*)header.header, header.getHeaderLength()); + + if (m_OutBuffer->space() >= pct.size() + header.getHeaderLength() && msg_queue()->is_empty()) + { + // Put the packet on the buffer. + if (m_OutBuffer->copy((char*) header.header, header.getHeaderLength()) == -1) + ACE_ASSERT (false); + + if (!pct.empty()) + if (m_OutBuffer->copy((char*) pct.contents(), pct.size()) == -1) + ACE_ASSERT (false); + } + else + { + // Enqueue the packet. + ACE_Message_Block* mb; + + ACE_NEW_RETURN(mb, ACE_Message_Block(pct.size() + header.getHeaderLength()), -1); + + mb->copy((char*) header.header, header.getHeaderLength()); + + if (!pct.empty()) + mb->copy((const char*)pct.contents(), pct.size()); + + if (msg_queue()->enqueue_tail(mb,(ACE_Time_Value*)&ACE_Time_Value::zero) == -1) + { + sLog.outError("WorldSocket::SendPacket enqueue_tail failed"); + mb->release(); + return -1; + } + } + + return 0; +} + +long WorldSocket::AddReference (void) +{ + return static_cast<long> (add_reference()); +} + +long WorldSocket::RemoveReference (void) +{ + return static_cast<long> (remove_reference()); +} + +int WorldSocket::open (void *a) +{ + ACE_UNUSED_ARG (a); + + // Prevent double call to this func. + if (m_OutBuffer) + return -1; + + // This will also prevent the socket from being Updated + // while we are initializing it. + m_OutActive = true; + + // Hook for the manager. + if (sWorldSocketMgr->OnSocketOpen(this) == -1) + return -1; + + // Allocate the buffer. + ACE_NEW_RETURN (m_OutBuffer, ACE_Message_Block (m_OutBufferSize), -1); + + // Store peer address. + ACE_INET_Addr remote_addr; + + if (peer().get_remote_addr(remote_addr) == -1) + { + sLog.outError ("WorldSocket::open: peer().get_remote_addr errno = %s", ACE_OS::strerror (errno)); + return -1; + } + + m_Address = remote_addr.get_host_addr(); + + // Send startup packet. + WorldPacket packet (SMSG_AUTH_CHALLENGE, 24); + packet << uint32(1); // 1...31 + packet << m_Seed; + + BigNumber seed1; + seed1.SetRand(16 * 8); + packet.append(seed1.AsByteArray(16), 16); // new encryption seeds + + BigNumber seed2; + seed2.SetRand(16 * 8); + packet.append(seed2.AsByteArray(16), 16); // new encryption seeds + + if (SendPacket(packet) == -1) + return -1; + + // Register with ACE Reactor + if (reactor()->register_handler(this, ACE_Event_Handler::READ_MASK | ACE_Event_Handler::WRITE_MASK) == -1) + { + sLog.outError ("WorldSocket::open: unable to register client handler errno = %s", ACE_OS::strerror (errno)); + return -1; + } + + // reactor takes care of the socket from now on + remove_reference(); + + return 0; +} + +int WorldSocket::close (int) +{ + shutdown(); + + closing_ = true; + + remove_reference(); + + return 0; +} + +int WorldSocket::handle_input (ACE_HANDLE) +{ + if (closing_) + return -1; + + switch (handle_input_missing_data()) + { + case -1 : + { + if ((errno == EWOULDBLOCK) || + (errno == EAGAIN)) + { + return Update(); // interesting line ,isn't it ? + } + + DEBUG_LOG("WorldSocket::handle_input: Peer error closing connection errno = %s", ACE_OS::strerror (errno)); + + errno = ECONNRESET; + return -1; + } + case 0: + { + DEBUG_LOG("WorldSocket::handle_input: Peer has closed connection"); + + errno = ECONNRESET; + return -1; + } + case 1: + return 1; + default: + return Update(); // another interesting line ;) + } + + ACE_NOTREACHED(return -1); +} + +int WorldSocket::handle_output (ACE_HANDLE) +{ + ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1); + + if (closing_) + return -1; + + size_t send_len = m_OutBuffer->length(); + + if (send_len == 0) + return handle_output_queue(Guard); + +#ifdef MSG_NOSIGNAL + ssize_t n = peer().send (m_OutBuffer->rd_ptr(), send_len, MSG_NOSIGNAL); +#else + ssize_t n = peer().send (m_OutBuffer->rd_ptr(), send_len); +#endif // MSG_NOSIGNAL + + if (n == 0) + return -1; + else if (n == -1) + { + if (errno == EWOULDBLOCK || errno == EAGAIN) + return schedule_wakeup_output (Guard); + + return -1; + } + else if (n < (ssize_t)send_len) //now n > 0 + { + m_OutBuffer->rd_ptr (static_cast<size_t> (n)); + + // move the data to the base of the buffer + m_OutBuffer->crunch(); + + return schedule_wakeup_output (Guard); + } + else //now n == send_len + { + m_OutBuffer->reset(); + + return handle_output_queue (Guard); + } + + ACE_NOTREACHED (return 0); +} + +int WorldSocket::handle_output_queue (GuardType& g) +{ + if (msg_queue()->is_empty()) + return cancel_wakeup_output(g); + + ACE_Message_Block *mblk; + + if (msg_queue()->dequeue_head(mblk, (ACE_Time_Value*)&ACE_Time_Value::zero) == -1) + { + sLog.outError("WorldSocket::handle_output_queue dequeue_head"); + return -1; + } + + const size_t send_len = mblk->length(); + +#ifdef MSG_NOSIGNAL + ssize_t n = peer().send (mblk->rd_ptr(), send_len, MSG_NOSIGNAL); +#else + ssize_t n = peer().send (mblk->rd_ptr(), send_len); +#endif // MSG_NOSIGNAL + + if (n == 0) + { + mblk->release(); + + return -1; + } + else if (n == -1) + { + if (errno == EWOULDBLOCK || errno == EAGAIN) + { + msg_queue()->enqueue_head(mblk, (ACE_Time_Value*) &ACE_Time_Value::zero); + return schedule_wakeup_output (g); + } + + mblk->release(); + return -1; + } + else if (n < (ssize_t)send_len) //now n > 0 + { + mblk->rd_ptr(static_cast<size_t> (n)); + + if (msg_queue()->enqueue_head(mblk, (ACE_Time_Value*) &ACE_Time_Value::zero) == -1) + { + sLog.outError("WorldSocket::handle_output_queue enqueue_head"); + mblk->release(); + return -1; + } + + return schedule_wakeup_output (g); + } + else //now n == send_len + { + mblk->release(); + + return msg_queue()->is_empty() ? cancel_wakeup_output(g) : ACE_Event_Handler::WRITE_MASK; + } + + ACE_NOTREACHED(return -1); +} + +int WorldSocket::handle_close (ACE_HANDLE h, ACE_Reactor_Mask) +{ + // Critical section + { + ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1); + + closing_ = true; + + if (h == ACE_INVALID_HANDLE) + peer().close_writer(); + } + + // Critical section + { + ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1); + + m_Session = NULL; + } + + return 0; +} + +int WorldSocket::Update (void) +{ + if (closing_) + return -1; + + if (m_OutActive || (m_OutBuffer->length() == 0 && msg_queue()->is_empty())) + return 0; + + int ret; + do + ret = handle_output (get_handle()); + while (ret > 0); + + return ret; +} + +int WorldSocket::handle_input_header (void) +{ + ACE_ASSERT (m_RecvWPct == NULL); + + ACE_ASSERT (m_Header.length() == sizeof(ClientPktHeader)); + + m_Crypt.DecryptRecv ((uint8*) m_Header.rd_ptr(), sizeof(ClientPktHeader)); + + ClientPktHeader& header = *((ClientPktHeader*) m_Header.rd_ptr()); + + EndianConvertReverse(header.size); + EndianConvert(header.cmd); + + if ((header.size < 4) || (header.size > 10240) || (header.cmd > 10240)) + { + sLog.outError ("WorldSocket::handle_input_header: client sent malformed packet size = %d , cmd = %d", + header.size, header.cmd); + + errno = EINVAL; + return -1; + } + + header.size -= 4; + + ACE_NEW_RETURN (m_RecvWPct, WorldPacket ((uint16) header.cmd, header.size), -1); + + if (header.size > 0) + { + m_RecvWPct->resize (header.size); + m_RecvPct.base ((char*) m_RecvWPct->contents(), m_RecvWPct->size()); + } + else + { + ACE_ASSERT(m_RecvPct.space() == 0); + } + + return 0; +} + +int WorldSocket::handle_input_payload (void) +{ + // set errno properly here on error !!! + // now have a header and payload + + ACE_ASSERT (m_RecvPct.space() == 0); + ACE_ASSERT (m_Header.space() == 0); + ACE_ASSERT (m_RecvWPct != NULL); + + const int ret = ProcessIncoming (m_RecvWPct); + + m_RecvPct.base (NULL, 0); + m_RecvPct.reset(); + m_RecvWPct = NULL; + + m_Header.reset(); + + if (ret == -1) + errno = EINVAL; + + return ret; +} + +int WorldSocket::handle_input_missing_data (void) +{ + char buf [4096]; + + ACE_Data_Block db (sizeof (buf), + ACE_Message_Block::MB_DATA, + buf, + 0, + 0, + ACE_Message_Block::DONT_DELETE, + 0); + + ACE_Message_Block message_block(&db, + ACE_Message_Block::DONT_DELETE, + 0); + + const size_t recv_size = message_block.space(); + + const ssize_t n = peer().recv (message_block.wr_ptr(), + recv_size); + + if (n <= 0) + return n; + + message_block.wr_ptr (n); + + while (message_block.length() > 0) + { + if (m_Header.space() > 0) + { + //need to receive the header + const size_t to_header = (message_block.length() > m_Header.space() ? m_Header.space() : message_block.length()); + m_Header.copy (message_block.rd_ptr(), to_header); + message_block.rd_ptr (to_header); + + if (m_Header.space() > 0) + { + // Couldn't receive the whole header this time. + ACE_ASSERT (message_block.length() == 0); + errno = EWOULDBLOCK; + return -1; + } + + // We just received nice new header + if (handle_input_header() == -1) + { + ACE_ASSERT ((errno != EWOULDBLOCK) && (errno != EAGAIN)); + return -1; + } + } + + // Its possible on some error situations that this happens + // for example on closing when epoll receives more chunked data and stuff + // hope this is not hack ,as proper m_RecvWPct is asserted around + if (!m_RecvWPct) + { + sLog.outError ("Forcing close on input m_RecvWPct = NULL"); + errno = EINVAL; + return -1; + } + + // We have full read header, now check the data payload + if (m_RecvPct.space() > 0) + { + //need more data in the payload + const size_t to_data = (message_block.length() > m_RecvPct.space() ? m_RecvPct.space() : message_block.length()); + m_RecvPct.copy (message_block.rd_ptr(), to_data); + message_block.rd_ptr (to_data); + + if (m_RecvPct.space() > 0) + { + // Couldn't receive the whole data this time. + ACE_ASSERT (message_block.length() == 0); + errno = EWOULDBLOCK; + return -1; + } + } + + //just received fresh new payload + if (handle_input_payload() == -1) + { + ACE_ASSERT ((errno != EWOULDBLOCK) && (errno != EAGAIN)); + return -1; + } + } + + return n == recv_size ? 1 : 2; +} + +int WorldSocket::cancel_wakeup_output (GuardType& g) +{ + if (!m_OutActive) + return 0; + + m_OutActive = false; + + g.release(); + + if (reactor()->cancel_wakeup + (this, ACE_Event_Handler::WRITE_MASK) == -1) + { + // would be good to store errno from reactor with errno guard + sLog.outError ("WorldSocket::cancel_wakeup_output"); + return -1; + } + + return 0; +} + +int WorldSocket::schedule_wakeup_output (GuardType& g) +{ + if (m_OutActive) + return 0; + + m_OutActive = true; + + g.release(); + + if (reactor()->schedule_wakeup + (this, ACE_Event_Handler::WRITE_MASK) == -1) + { + sLog.outError ("WorldSocket::schedule_wakeup_output"); + return -1; + } + + return 0; +} + +int WorldSocket::ProcessIncoming (WorldPacket* new_pct) +{ + ACE_ASSERT (new_pct); + + // manage memory ;) + ACE_Auto_Ptr<WorldPacket> aptr (new_pct); + + const ACE_UINT16 opcode = new_pct->GetOpcode(); + + if (closing_) + return -1; + + // Dump received packet. + if (sWorldLog.LogWorld()) + { + sWorldLog.outTimestampLog ("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.outLog ("%.2X ", (*new_pct)[p++]); + + sWorldLog.outLog ("\n"); + } + sWorldLog.outLog ("\n"); + } + + try { + switch(opcode) + { + case CMSG_PING: + return HandlePing (*new_pct); + case CMSG_AUTH_SESSION: + if (m_Session) + { + sLog.outError ("WorldSocket::ProcessIncoming: Player send CMSG_AUTH_SESSION again"); + return -1; + } + + return HandleAuthSession (*new_pct); + case CMSG_KEEP_ALIVE: + DEBUG_LOG ("CMSG_KEEP_ALIVE ,size: %d", new_pct->size()); + + return 0; + default: + { + ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1); + + if (m_Session != NULL) + { + // Our Idle timer will reset on any non PING opcodes. + // Catches people idling on the login screen and any lingering ingame connections. + m_Session->ResetTimeOutTime(); + + // OK ,give the packet to WorldSession + aptr.release(); + // 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 = %u", uint32(opcode)); + return -1; + } + } + } + } + catch(ByteBufferException &) + { + sLog.outError("WorldSocket::ProcessIncoming ByteBufferException occured while parsing an instant handled packet (opcode: %u) from client %s, accountid=%i. Disconnected client.", + opcode, GetRemoteAddress().c_str(), m_Session?m_Session->GetAccountId():-1); + if (sLog.IsOutDebug()) + { + sLog.outDebug("Dumping error causing packet:"); + new_pct->hexlike(); + } + + return -1; + } + + ACE_NOTREACHED (return 0); +} + +int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) +{ + // NOTE: ATM the socket is singlethread, have this in mind ... + uint8 digest[20]; + uint32 clientSeed; + uint32 unk2, unk3; + uint64 unk4; + uint32 BuiltNumberClient; + uint32 id, security; + //uint8 expansion = 0; + LocaleConstant locale; + std::string account; + Sha1Hash sha1; + BigNumber v, s, g, N; + WorldPacket packet, SendAddonPacked; + + BigNumber K; + + if (sWorld.IsClosed()) + { + packet.Initialize(SMSG_AUTH_RESPONSE, 1); + packet << uint8(AUTH_REJECT); + SendPacket (packet); + + sLog.outError ("WorldSocket::HandleAuthSession: World closed, denying client (%s).", m_Session->GetRemoteAddress().c_str()); + return -1; + } + + // Read the content of the packet + recvPacket >> BuiltNumberClient; // for now no use + recvPacket >> unk2; + recvPacket >> account; + recvPacket >> unk3; + recvPacket >> clientSeed; + recvPacket >> unk4; + recvPacket.read (digest, 20); + + DEBUG_LOG ("WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, unk3 %u, clientseed %u", + BuiltNumberClient, + unk2, + account.c_str(), + unk3, + clientSeed); + + // 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_AutoPtr result = + LoginDatabase.PQuery ("SELECT " + "id, " //0 + "sessionkey, " //1 + "last_ip, " //2 + "locked, " //3 + "v, " //4 + "s, " //5 + "expansion, " //6 + "mutetime, " //7 + "locale " //8 + "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(); + + uint8 expansion = fields[6].GetUInt8(); + uint32 world_expansion = sWorld.getConfig(CONFIG_EXPANSION); + if (expansion > world_expansion) + expansion = world_expansion; + //expansion = ((sWorld.getConfig(CONFIG_EXPANSION) > fields[6].GetUInt8()) ? fields[6].GetUInt8() : sWorld.getConfig(CONFIG_EXPANSION)); + + N.SetHexStr ("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); + g.SetDword (7); + + v.SetHexStr(fields[4].GetString()); + s.SetHexStr (fields[5].GetString()); + + const char* sStr = s.AsHexStr(); //Must be freed by OPENSSL_free() + const char* vStr = v.AsHexStr(); //Must be freed by OPENSSL_free() + + DEBUG_LOG ("WorldSocket::HandleAuthSession: (s,v) check s: %s v: %s", + sStr, + vStr); + + OPENSSL_free ((void*) sStr); + OPENSSL_free ((void*) vStr); + + ///- Re-check ip locking (same check as in realmd). + if (fields[3].GetUInt8() == 1) // if ip is locked + { + if (strcmp (fields[2].GetString(), GetRemoteAddress().c_str())) + { + packet.Initialize (SMSG_AUTH_RESPONSE, 1); + packet << uint8 (AUTH_FAILED); + SendPacket (packet); + + sLog.outBasic ("WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs)."); + return -1; + } + } + + id = fields[0].GetUInt32(); + /* + if (security > SEC_ADMINISTRATOR) // prevent invalid security settings in DB + security = SEC_ADMINISTRATOR; + */ + + K.SetHexStr (fields[1].GetString()); + + time_t mutetime = time_t (fields[7].GetUInt64()); + + locale = LocaleConstant (fields[8].GetUInt8()); + if (locale >= MAX_LOCALE) + locale = LOCALE_enUS; + + // Checks gmlevel per Realm + result = + LoginDatabase.PQuery ("SELECT " + "RealmID, " //0 + "gmlevel " //1 + "FROM account_access " + "WHERE id = '%d'" + " AND (RealmID = '%d'" + " OR RealmID = '-1')", + id, realmID); + if (!result) + security = 0; + else + { + fields = result->Fetch(); + security = fields[1].GetInt32(); + } + + // Re-check account ban (same check as in realmd) + QueryResult_AutoPtr banresult = + LoginDatabase.PQuery ("SELECT 1 FROM account_banned WHERE id = %u AND active = 1 " + "UNION " + "SELECT 1 FROM ip_banned WHERE ip = '%s'", + id, GetRemoteAddress().c_str()); + + if (banresult) // if account banned + { + packet.Initialize (SMSG_AUTH_RESPONSE, 1); + packet << uint8 (AUTH_BANNED); + SendPacket (packet); + + sLog.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)."); + return -1; + } + + // Check locked state for server + sWorld.UpdateAllowedSecurity(); + AccountTypes allowedAccountType = sWorld.GetPlayerSecurityLimit(); + sLog.outDebug("Allowed Level: %u Player Level %u", allowedAccountType, AccountTypes(security)); + if (allowedAccountType > SEC_PLAYER && AccountTypes(security) < allowedAccountType) + { + WorldPacket Packet (SMSG_AUTH_RESPONSE, 1); + Packet << uint8 (AUTH_UNAVAILABLE); + + SendPacket (packet); + + sLog.outDetail ("WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); + return -1; + } + + // 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; + } + + std::string address = 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()); + + // NOTE ATM the socket is single-threaded, have this in mind ... + ACE_NEW_RETURN (m_Session, WorldSession (id, this, AccountTypes(security), expansion, mutetime, locale), -1); + + m_Crypt.Init(&K); + + m_Session->LoadGlobalAccountData(); + m_Session->LoadTutorialsData(); + m_Session->ReadAddonsInfo(recvPacket); + + // Sleep this Network thread for + uint32 sleepTime = sWorld.getConfig(CONFIG_SESSION_ADD_DELAY); + ACE_OS::sleep (ACE_Time_Value (0, sleepTime)); + + sWorld.AddSession (m_Session); + + return 0; +} + +int WorldSocket::HandlePing (WorldPacket& recvPacket) +{ + uint32 ping; + uint32 latency; + + // Get the ping packet content + recvPacket >> ping; + recvPacket >> latency; + + if (m_LastPingTime == ACE_Time_Value::zero) + m_LastPingTime = ACE_OS::gettimeofday(); // for 1st ping + else + { + ACE_Time_Value cur_time = ACE_OS::gettimeofday(); + ACE_Time_Value diff_time (cur_time); + diff_time -= m_LastPingTime; + m_LastPingTime = cur_time; + + if (diff_time < ACE_Time_Value (27)) + { + ++m_OverSpeedPings; + + uint32 max_count = sWorld.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 " + "over-speed pings address = %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," + " address = %s", + GetRemoteAddress().c_str()); + return -1; + } + } + + WorldPacket packet (SMSG_PONG, 4); + packet << ping; + return SendPacket (packet); +} |