/* * Copyright (C) 2005-2009 MaNGOS * * Copyright (C) 2008-2010 Trinity * * 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 #include #include #include #include #include #include #include #include #include #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 (rand32 ())), m_OverSpeedPings (0), m_LastPingTime (ACE_Time_Value::zero), m_TimeOutTime (0) { reference_counting_policy ().value (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); } WorldSocket::~WorldSocket (void) { if (m_RecvWPct) delete m_RecvWPct; if (m_OutBuffer) m_OutBuffer->release (); closing_ = true; peer ().close (); WorldPacket* pct; while (m_PacketQueue.dequeue_head (pct) == 0) delete pct; } 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(pct)[p++]); sWorldLog.outLog ("\n"); } sWorldLog.outLog ("\n"); } // don't try to send the packet if there are packets on the queue if (!m_PacketQueue.is_empty() || iSendPacket(pct) == -1) { WorldPacket* npct; ACE_NEW_RETURN (npct, WorldPacket (pct), -1); // 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) { delete npct; sLog.outError ("WorldSocket::SendPacket: m_PacketQueue.enqueue_tail failed"); return -1; } // iSendPacket may fail if the packet is larger than the buffer (even in the buffer is empty) // So we must try to flush so the packet may be sent partially // don't cancel_wakeup_output here if (iFlushPacketQueue()) return schedule_wakeup_output(Guard); } return 0; } long WorldSocket::AddReference (void) { return static_cast (add_reference ()); } long WorldSocket::RemoveReference (void) { return static_cast (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; packet << uint32(0xF3539DA3); // random data packet << uint32(0x6E8547B9); // random data packet << uint32(0x9A6AA2F8); // random data packet << uint32(0xA4F170F4); // random data 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) { if (!iFlushPacketQueue()) return cancel_wakeup_output(Guard); send_len = m_OutBuffer->length (); } #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 < send_len) //now n > 0 { m_OutBuffer->rd_ptr (static_cast (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 (); if (!iFlushPacketQueue ()) return cancel_wakeup_output (Guard); else return schedule_wakeup_output (Guard); } ACE_NOTREACHED (return 0); } 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_TimeOutTime && time(NULL) >= m_TimeOutTime) return -1; if (m_OutActive || m_OutBuffer->length () == 0) return 0; return handle_output (get_handle ()); } 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 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); /* The m_TimeOutTime measure is put in to be able to automatically disconnect connections that are sitting idle on the character select screen. After a period of being AFK in the realm, the client will be automatically sent back to the character selection screen. In order to pick up the idle connections and prevent they are sitting there, taking up slots for the realm, we'll check if the packet that was sent is CMSG_CHAR_ENUM and initiate the timeout timer that will be checked on WorldSocket::Update. */ if (opcode == CMSG_CHAR_ENUM) m_TimeOutTime = time(NULL) + sWorld.getConfig(CONFIG_SOCKET_TIMEOUTTIME) / IN_MILISECONDS; /* If the packet is CMSG_PLAYER_LOGIN opcode, it means our connection is not idle, we're logging into the world. Until we receive our next CMSG_CHAR_ENUM packet, we can disregard the timeout timer. */ else if (opcode == CMSG_PLAYER_LOGIN) m_TimeOutTime = 0; 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 = %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); // In case needed sometime the second arg is in microseconds 1 000 000 = 1 sec ACE_OS::sleep (ACE_Time_Value (0, 10000)); 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); } int WorldSocket::iSendPacket (const WorldPacket& pct) { ServerPktHeader header(pct.size()+2, pct.GetOpcode()); if (m_OutBuffer->space() < pct.size () + header.getHeaderLength()) { errno = ENOBUFS; return -1; } m_Crypt.EncryptSend ( header.header, header.getHeaderLength()); 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); return 0; } /// Return 1 if some bytes written and packet completed /// Return 0 if no byte written, but still remaining data /// Return -1 if some bytes written and still remaining data int WorldSocket::iSendPartialPacket(WorldPacket& pct) { size_t remainingLen = pct.size() - pct.rpos(); if (pct.rpos() == 0) // nothing sent yet => send header { ServerPktHeader header(pct.size()+2, pct.GetOpcode()); // check if there is at least enough space for the header if (m_OutBuffer->space () < header.getHeaderLength()) return 0; // nothing written, still remaining data => 0 m_Crypt.EncryptSend(header.header, header.getHeaderLength()); if (m_OutBuffer->copy((char*) header.header, header.getHeaderLength()) == -1) return 0; // no space left. This should not happend. Just return 0 as nothing written and still remaining data if (remainingLen == 0) return 1; // header written, nothing else => 1 } if ((m_OutBuffer->space()) < remainingLen) { size_t len = m_OutBuffer->space(); if (m_OutBuffer->copy((char*) (pct.contents() + pct.rpos()), len) == -1) ACE_ASSERT (false); pct.read_skip(len); return -1; // packet will be pushed back on the queue } if (m_OutBuffer->copy((char*) (pct.contents() + pct.rpos()), remainingLen) == -1) ACE_ASSERT (false); return 1; // some byte written and packet completed } bool WorldSocket::iFlushPacketQueue() { WorldPacket *pct; bool haveone = false; while (m_PacketQueue.dequeue_head(pct) == 0) { int result = iSendPartialPacket(*pct); if (result != 0) { // some bytes were written haveone = true; } if (result <= 0) { if (m_PacketQueue.enqueue_head(pct) == -1) { delete pct; sLog.outError ("WorldSocket::iFlushPacketQueue m_PacketQueue->enqueue_head"); return false; } break; } else { // packet completed delete pct; } } return haveone; }