2 * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include <ace/Message_Block.h>
20 #include <ace/OS_NS_string.h>
21 #include <ace/OS_NS_unistd.h>
22 #include <ace/os_include/arpa/os_inet.h>
23 #include <ace/os_include/netinet/os_tcp.h>
24 #include <ace/os_include/sys/os_types.h>
25 #include <ace/os_include/sys/os_socket.h>
26 #include <ace/OS_NS_string.h>
27 #include <ace/Reactor.h>
28 #include <ace/Auto_Ptr.h>
30 #include "WorldSocket.h"
35 #include "WorldPacket.h"
36 #include "SharedDefines.h"
37 #include "ByteBuffer.h"
39 #include "Database/DatabaseEnv.h"
40 #include "Auth/BigNumber.h"
41 #include "Auth/Sha1.h"
42 #include "WorldSession.h"
43 #include "WorldSocketMgr.h"
45 #include "DBCStores.h"
47 #if defined( __GNUC__ )
53 struct ServerPktHeader
56 * size is the length of the payload _plus_ the length of the opcode
58 ServerPktHeader(uint32 size
, uint16 cmd
) : size(size
)
63 sLog
.outDebug("initializing large server to client packet. Size: %u, cmd: %u", size
, cmd
);
64 header
[headerIndex
++] = 0x80|(0xFF &(size
>>16));
66 header
[headerIndex
++] = 0xFF &(size
>>8);
67 header
[headerIndex
++] = 0xFF &size
;
69 header
[headerIndex
++] = 0xFF & cmd
;
70 header
[headerIndex
++] = 0xFF & (cmd
>>8);
73 uint8
getHeaderLength()
75 // cmd = 2 bytes, size= 2||3bytes
76 return 2+(isLargePacket()?3:2);
88 struct ClientPktHeader
94 #if defined( __GNUC__ )
100 WorldSocket::WorldSocket (void) :
105 m_Header (sizeof (ClientPktHeader
)),
107 m_OutBufferSize (65536),
109 m_Seed (static_cast<uint32
> (rand32 ())),
110 m_OverSpeedPings (0),
111 m_LastPingTime (ACE_Time_Value::zero
)
113 reference_counting_policy ().value (ACE_Event_Handler::Reference_Counting_Policy::ENABLED
);
115 msg_queue()->high_water_mark(8*1024*1024);
116 msg_queue()->low_water_mark(8*1024*1024);
119 WorldSocket::~WorldSocket (void)
125 m_OutBuffer
->release ();
132 bool WorldSocket::IsClosed (void) const
137 void WorldSocket::CloseSocket (void)
140 ACE_GUARD (LockType
, Guard
, m_OutBufferLock
);
146 peer ().close_writer ();
150 ACE_GUARD (LockType
, Guard
, m_SessionLock
);
156 const std::string
& WorldSocket::GetRemoteAddress (void) const
161 int WorldSocket::SendPacket (const WorldPacket
& pct
)
163 ACE_GUARD_RETURN (LockType
, Guard
, m_OutBufferLock
, -1);
168 // Dump outgoing packet.
169 sLog
.outWorldPacketDump(uint32(get_handle()), pct
.GetOpcode(), LookupOpcodeName(pct
.GetOpcode()), &pct
, false);
171 ServerPktHeader
header(pct
.size()+2, pct
.GetOpcode());
172 m_Crypt
.EncryptSend ((uint8
*)header
.header
, header
.getHeaderLength());
174 if (m_OutBuffer
->space () >= pct
.size () + header
.getHeaderLength() && msg_queue()->is_empty())
176 // Put the packet on the buffer.
177 if (m_OutBuffer
->copy ((char*) header
.header
, header
.getHeaderLength()) == -1)
181 if (m_OutBuffer
->copy ((char*) pct
.contents (), pct
.size ()) == -1)
186 // Enqueue the packet.
187 ACE_Message_Block
* mb
;
189 ACE_NEW_RETURN(mb
, ACE_Message_Block(pct
.size () + header
.getHeaderLength()), -1);
191 mb
->copy((char*) header
.header
, header
.getHeaderLength());
194 mb
->copy((const char*)pct
.contents(), pct
.size ());
196 if(msg_queue()->enqueue_tail(mb
,(ACE_Time_Value
*)&ACE_Time_Value::zero
) == -1)
198 sLog
.outError("WorldSocket::SendPacket enqueue_tail");
207 long WorldSocket::AddReference (void)
209 return static_cast<long> (add_reference ());
212 long WorldSocket::RemoveReference (void)
214 return static_cast<long> (remove_reference ());
217 int WorldSocket::open (void *a
)
221 // Prevent double call to this func.
225 // This will also prevent the socket from being Updated
226 // while we are initializing it.
229 // Hook for the manager.
230 if (sWorldSocketMgr
->OnSocketOpen (this) == -1)
233 // Allocate the buffer.
234 ACE_NEW_RETURN (m_OutBuffer
, ACE_Message_Block (m_OutBufferSize
), -1);
236 // Store peer address.
237 ACE_INET_Addr remote_addr
;
239 if (peer ().get_remote_addr (remote_addr
) == -1)
241 sLog
.outError ("WorldSocket::open: peer ().get_remote_addr errno = %s", ACE_OS::strerror (errno
));
245 m_Address
= remote_addr
.get_host_addr ();
247 // Send startup packet.
248 WorldPacket
packet (SMSG_AUTH_CHALLENGE
, 24);
249 packet
<< uint32(1); // 1...31
251 packet
<< uint32(0xF3539DA3); // random data
252 packet
<< uint32(0x6E8547B9); // random data
253 packet
<< uint32(0x9A6AA2F8); // random data
254 packet
<< uint32(0xA4F170F4); // random data
256 if (SendPacket (packet
) == -1)
259 // Register with ACE Reactor
260 if (reactor ()->register_handler(this, ACE_Event_Handler::READ_MASK
| ACE_Event_Handler::WRITE_MASK
) == -1)
262 sLog
.outError ("WorldSocket::open: unable to register client handler errno = %s", ACE_OS::strerror (errno
));
266 // reactor takes care of the socket from now on
272 int WorldSocket::close (int)
283 int WorldSocket::handle_input (ACE_HANDLE
)
288 switch (handle_input_missing_data ())
292 if ((errno
== EWOULDBLOCK
) ||
295 return Update (); // interesting line ,isn't it ?
298 DEBUG_LOG ("WorldSocket::handle_input: Peer error closing connection errno = %s", ACE_OS::strerror (errno
));
305 DEBUG_LOG ("WorldSocket::handle_input: Peer has closed connection");
313 return Update (); // another interesting line ;)
316 ACE_NOTREACHED(return -1);
319 int WorldSocket::handle_output (ACE_HANDLE
)
321 ACE_GUARD_RETURN (LockType
, Guard
, m_OutBufferLock
, -1);
326 const size_t send_len
= m_OutBuffer
->length ();
329 return handle_output_queue (Guard
);
332 ssize_t n
= peer ().send (m_OutBuffer
->rd_ptr (), send_len
, MSG_NOSIGNAL
);
334 ssize_t n
= peer ().send (m_OutBuffer
->rd_ptr (), send_len
);
335 #endif // MSG_NOSIGNAL
341 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
342 return schedule_wakeup_output (Guard
);
346 else if (n
< (ssize_t
)send_len
) //now n > 0
348 m_OutBuffer
->rd_ptr (static_cast<size_t> (n
));
350 // move the data to the base of the buffer
351 m_OutBuffer
->crunch ();
353 return schedule_wakeup_output (Guard
);
355 else //now n == send_len
357 m_OutBuffer
->reset ();
359 return handle_output_queue (Guard
);
362 ACE_NOTREACHED (return 0);
365 int WorldSocket::handle_output_queue (GuardType
& g
)
367 if(msg_queue()->is_empty())
368 return cancel_wakeup_output(g
);
370 ACE_Message_Block
*mblk
;
372 if(msg_queue()->dequeue_head(mblk
, (ACE_Time_Value
*)&ACE_Time_Value::zero
) == -1)
374 sLog
.outError("WorldSocket::handle_output_queue dequeue_head");
378 const size_t send_len
= mblk
->length ();
381 ssize_t n
= peer ().send (mblk
->rd_ptr (), send_len
, MSG_NOSIGNAL
);
383 ssize_t n
= peer ().send (mblk
->rd_ptr (), send_len
);
384 #endif // MSG_NOSIGNAL
394 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
396 msg_queue()->enqueue_head(mblk
, (ACE_Time_Value
*) &ACE_Time_Value::zero
);
397 return schedule_wakeup_output (g
);
403 else if (n
< (ssize_t
)send_len
) //now n > 0
405 mblk
->rd_ptr (static_cast<size_t> (n
));
407 if (msg_queue()->enqueue_head(mblk
, (ACE_Time_Value
*) &ACE_Time_Value::zero
) == -1)
409 sLog
.outError("WorldSocket::handle_output_queue enqueue_head");
414 return schedule_wakeup_output (g
);
416 else //now n == send_len
420 return msg_queue()->is_empty() ? cancel_wakeup_output(g
) : ACE_Event_Handler::WRITE_MASK
;
423 ACE_NOTREACHED(return -1);
426 int WorldSocket::handle_close (ACE_HANDLE h
, ACE_Reactor_Mask
)
430 ACE_GUARD_RETURN (LockType
, Guard
, m_OutBufferLock
, -1);
434 if (h
== ACE_INVALID_HANDLE
)
435 peer ().close_writer ();
440 ACE_GUARD_RETURN (LockType
, Guard
, m_SessionLock
, -1);
448 int WorldSocket::Update (void)
453 if (m_OutActive
|| (m_OutBuffer
->length () == 0 && msg_queue()->is_empty()))
458 ret
= handle_output (get_handle ());
464 int WorldSocket::handle_input_header (void)
466 ACE_ASSERT (m_RecvWPct
== NULL
);
468 ACE_ASSERT (m_Header
.length () == sizeof (ClientPktHeader
));
470 m_Crypt
.DecryptRecv ((uint8
*) m_Header
.rd_ptr (), sizeof (ClientPktHeader
));
472 ClientPktHeader
& header
= *((ClientPktHeader
*) m_Header
.rd_ptr ());
474 EndianConvertReverse(header
.size
);
475 EndianConvert(header
.cmd
);
477 if ((header
.size
< 4) || (header
.size
> 10240) || (header
.cmd
> 10240))
479 sLog
.outError ("WorldSocket::handle_input_header: client sent malformed packet size = %d , cmd = %d",
480 header
.size
, header
.cmd
);
488 ACE_NEW_RETURN (m_RecvWPct
, WorldPacket ((uint16
) header
.cmd
, header
.size
), -1);
492 m_RecvWPct
->resize (header
.size
);
493 m_RecvPct
.base ((char*) m_RecvWPct
->contents (), m_RecvWPct
->size ());
497 ACE_ASSERT(m_RecvPct
.space() == 0);
503 int WorldSocket::handle_input_payload (void)
505 // set errno properly here on error !!!
506 // now have a header and payload
508 ACE_ASSERT (m_RecvPct
.space () == 0);
509 ACE_ASSERT (m_Header
.space () == 0);
510 ACE_ASSERT (m_RecvWPct
!= NULL
);
512 const int ret
= ProcessIncoming (m_RecvWPct
);
514 m_RecvPct
.base (NULL
, 0);
526 int WorldSocket::handle_input_missing_data (void)
530 ACE_Data_Block
db ( sizeof (buf
),
531 ACE_Message_Block::MB_DATA
,
535 ACE_Message_Block::DONT_DELETE
,
538 ACE_Message_Block
message_block(&db
,
539 ACE_Message_Block::DONT_DELETE
,
542 const size_t recv_size
= message_block
.space ();
544 const ssize_t n
= peer ().recv (message_block
.wr_ptr (),
550 message_block
.wr_ptr (n
);
552 while (message_block
.length () > 0)
554 if (m_Header
.space () > 0)
556 //need to receive the header
557 const size_t to_header
= (message_block
.length () > m_Header
.space () ? m_Header
.space () : message_block
.length ());
558 m_Header
.copy (message_block
.rd_ptr (), to_header
);
559 message_block
.rd_ptr (to_header
);
561 if (m_Header
.space () > 0)
563 // Couldn't receive the whole header this time.
564 ACE_ASSERT (message_block
.length () == 0);
569 // We just received nice new header
570 if (handle_input_header () == -1)
572 ACE_ASSERT ((errno
!= EWOULDBLOCK
) && (errno
!= EAGAIN
));
577 // Its possible on some error situations that this happens
578 // for example on closing when epoll receives more chunked data and stuff
579 // hope this is not hack ,as proper m_RecvWPct is asserted around
582 sLog
.outError ("Forcing close on input m_RecvWPct = NULL");
587 // We have full read header, now check the data payload
588 if (m_RecvPct
.space () > 0)
590 //need more data in the payload
591 const size_t to_data
= (message_block
.length () > m_RecvPct
.space () ? m_RecvPct
.space () : message_block
.length ());
592 m_RecvPct
.copy (message_block
.rd_ptr (), to_data
);
593 message_block
.rd_ptr (to_data
);
595 if (m_RecvPct
.space () > 0)
597 // Couldn't receive the whole data this time.
598 ACE_ASSERT (message_block
.length () == 0);
604 //just received fresh new payload
605 if (handle_input_payload () == -1)
607 ACE_ASSERT ((errno
!= EWOULDBLOCK
) && (errno
!= EAGAIN
));
612 return n
== recv_size
? 1 : 2;
615 int WorldSocket::cancel_wakeup_output (GuardType
& g
)
624 if (reactor ()->cancel_wakeup
625 (this, ACE_Event_Handler::WRITE_MASK
) == -1)
627 // would be good to store errno from reactor with errno guard
628 sLog
.outError ("WorldSocket::cancel_wakeup_output");
635 int WorldSocket::schedule_wakeup_output (GuardType
& g
)
644 if (reactor ()->schedule_wakeup
645 (this, ACE_Event_Handler::WRITE_MASK
) == -1)
647 sLog
.outError ("WorldSocket::schedule_wakeup_output");
654 int WorldSocket::ProcessIncoming (WorldPacket
* new_pct
)
656 ACE_ASSERT (new_pct
);
659 ACE_Auto_Ptr
<WorldPacket
> aptr (new_pct
);
661 const ACE_UINT16 opcode
= new_pct
->GetOpcode ();
663 if (opcode
>= NUM_MSG_TYPES
)
665 sLog
.outError( "SESSION: received non-existed opcode 0x%.4X", opcode
);
672 // Dump received packet.
673 sLog
.outWorldPacketDump(uint32(get_handle()), new_pct
->GetOpcode(), LookupOpcodeName(new_pct
->GetOpcode()), new_pct
, true);
680 return HandlePing (*new_pct
);
681 case CMSG_AUTH_SESSION
:
684 sLog
.outError ("WorldSocket::ProcessIncoming: Player send CMSG_AUTH_SESSION again");
688 return HandleAuthSession (*new_pct
);
689 case CMSG_KEEP_ALIVE
:
690 DEBUG_LOG ("CMSG_KEEP_ALIVE ,size: %d", new_pct
->size ());
695 ACE_GUARD_RETURN (LockType
, Guard
, m_SessionLock
, -1);
697 if (m_Session
!= NULL
)
699 // OK ,give the packet to WorldSession
701 // WARNINIG here we call it with locks held.
702 // Its possible to cause deadlock if QueuePacket calls back
703 m_Session
->QueuePacket (new_pct
);
708 sLog
.outError ("WorldSocket::ProcessIncoming: Client not authed opcode = %u", uint32(opcode
));
714 catch (ByteBufferException
&)
716 sLog
.outError("WorldSocket::ProcessIncoming ByteBufferException occured while parsing an instant handled packet (opcode: %u) from client %s, accountid=%i.",
717 opcode
, GetRemoteAddress().c_str(), m_Session
?m_Session
->GetAccountId():-1);
718 if(sLog
.IsOutDebug())
720 sLog
.outDebug("Dumping error-causing packet:");
724 if (sWorld
.getConfig(CONFIG_BOOL_KICK_PLAYER_ON_BAD_PACKET
))
726 sLog
.outDetail("Disconnecting session [account id %i / address %s] for badly formatted packet.",
727 m_Session
?m_Session
->GetAccountId():-1, GetRemoteAddress().c_str());
735 ACE_NOTREACHED (return 0);
738 int WorldSocket::HandleAuthSession (WorldPacket
& recvPacket
)
740 // NOTE: ATM the socket is singlethread, have this in mind ...
748 LocaleConstant locale
;
751 BigNumber v
, s
, g
, N
;
752 WorldPacket packet
, SendAddonPacked
;
756 // Read the content of the packet
757 recvPacket
>> ClientBuild
;
759 recvPacket
>> account
;
761 recvPacket
>> clientSeed
;
763 recvPacket
.read (digest
, 20);
765 DEBUG_LOG ("WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, unk3 %u, clientseed %u",
772 // Check the version of client trying to connect
773 if(!IsAcceptableClientBuild(ClientBuild
))
775 packet
.Initialize (SMSG_AUTH_RESPONSE
, 1);
776 packet
<< uint8 (AUTH_VERSION_MISMATCH
);
780 sLog
.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (version mismatch).");
784 // Get the account information from the realmd database
785 std::string safe_account
= account
; // Duplicate, else will screw the SHA hash verification below
786 loginDatabase
.escape_string (safe_account
);
787 // No SQL injection, username escaped.
789 QueryResult
*result
=
790 loginDatabase
.PQuery ("SELECT "
802 "WHERE username = '%s'",
803 safe_account
.c_str ());
805 // Stop if the account is not found
808 packet
.Initialize (SMSG_AUTH_RESPONSE
, 1);
809 packet
<< uint8 (AUTH_UNKNOWN_ACCOUNT
);
813 sLog
.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (unknown account).");
817 Field
* fields
= result
->Fetch ();
819 expansion
= ((sWorld
.getConfig(CONFIG_UINT32_EXPANSION
) > fields
[7].GetUInt8()) ? fields
[7].GetUInt8() : sWorld
.getConfig(CONFIG_UINT32_EXPANSION
));
821 N
.SetHexStr ("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
824 v
.SetHexStr(fields
[5].GetString());
825 s
.SetHexStr (fields
[6].GetString ());
827 const char* sStr
= s
.AsHexStr (); //Must be freed by OPENSSL_free()
828 const char* vStr
= v
.AsHexStr (); //Must be freed by OPENSSL_free()
830 DEBUG_LOG ("WorldSocket::HandleAuthSession: (s,v) check s: %s v: %s",
834 OPENSSL_free ((void*) sStr
);
835 OPENSSL_free ((void*) vStr
);
837 ///- Re-check ip locking (same check as in realmd).
838 if (fields
[4].GetUInt8 () == 1) // if ip is locked
840 if (strcmp (fields
[3].GetString (), GetRemoteAddress ().c_str ()))
842 packet
.Initialize (SMSG_AUTH_RESPONSE
, 1);
843 packet
<< uint8 (AUTH_FAILED
);
847 sLog
.outBasic ("WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs).");
852 id
= fields
[0].GetUInt32 ();
853 security
= fields
[1].GetUInt16 ();
854 if(security
> SEC_ADMINISTRATOR
) // prevent invalid security settings in DB
855 security
= SEC_ADMINISTRATOR
;
857 K
.SetHexStr (fields
[2].GetString ());
859 time_t mutetime
= time_t (fields
[8].GetUInt64 ());
861 locale
= LocaleConstant (fields
[9].GetUInt8 ());
862 if (locale
>= MAX_LOCALE
)
863 locale
= LOCALE_enUS
;
867 // Re-check account ban (same check as in realmd)
868 QueryResult
*banresult
=
869 loginDatabase
.PQuery ("SELECT 1 FROM account_banned WHERE id = %u AND active = 1 AND (unbandate > UNIX_TIMESTAMP() OR unbandate = bandate)"
871 "SELECT 1 FROM ip_banned WHERE (unbandate = bandate OR unbandate > UNIX_TIMESTAMP()) AND ip = '%s'",
872 id
, GetRemoteAddress().c_str());
874 if (banresult
) // if account banned
876 packet
.Initialize (SMSG_AUTH_RESPONSE
, 1);
877 packet
<< uint8 (AUTH_BANNED
);
882 sLog
.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (Account banned).");
886 // Check locked state for server
887 AccountTypes allowedAccountType
= sWorld
.GetPlayerSecurityLimit ();
889 if (allowedAccountType
> SEC_PLAYER
&& AccountTypes(security
) < allowedAccountType
)
891 WorldPacket
Packet (SMSG_AUTH_RESPONSE
, 1);
892 Packet
<< uint8 (AUTH_UNAVAILABLE
);
896 sLog
.outBasic ("WorldSocket::HandleAuthSession: User tries to login but his security level is not enough");
900 // Check that Key and account name are the same on client and server
904 uint32 seed
= m_Seed
;
906 sha
.UpdateData (account
);
907 sha
.UpdateData ((uint8
*) & t
, 4);
908 sha
.UpdateData ((uint8
*) & clientSeed
, 4);
909 sha
.UpdateData ((uint8
*) & seed
, 4);
910 sha
.UpdateBigNumbers (&K
, NULL
);
913 if (memcmp (sha
.GetDigest (), digest
, 20))
915 packet
.Initialize (SMSG_AUTH_RESPONSE
, 1);
916 packet
<< uint8 (AUTH_FAILED
);
920 sLog
.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (authentification failed).");
924 std::string address
= GetRemoteAddress ();
926 DEBUG_LOG ("WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.",
930 // Update the last_ip in the database
931 // No SQL injection, username escaped.
932 loginDatabase
.escape_string (address
);
934 loginDatabase
.PExecute ("UPDATE account "
935 "SET last_ip = '%s' "
936 "WHERE username = '%s'",
938 safe_account
.c_str ());
940 // NOTE ATM the socket is single-threaded, have this in mind ...
941 ACE_NEW_RETURN (m_Session
, WorldSession (id
, this, AccountTypes(security
), expansion
, mutetime
, locale
), -1);
945 m_Session
->LoadGlobalAccountData();
946 m_Session
->LoadTutorialsData();
947 m_Session
->ReadAddonsInfo(recvPacket
);
949 // In case needed sometime the second arg is in microseconds 1 000 000 = 1 sec
950 ACE_OS::sleep (ACE_Time_Value (0, 10000));
952 sWorld
.AddSession (m_Session
);
957 int WorldSocket::HandlePing (WorldPacket
& recvPacket
)
962 // Get the ping packet content
964 recvPacket
>> latency
;
966 if (m_LastPingTime
== ACE_Time_Value::zero
)
967 m_LastPingTime
= ACE_OS::gettimeofday (); // for 1st ping
970 ACE_Time_Value cur_time
= ACE_OS::gettimeofday ();
971 ACE_Time_Value
diff_time (cur_time
);
972 diff_time
-= m_LastPingTime
;
973 m_LastPingTime
= cur_time
;
975 if (diff_time
< ACE_Time_Value (27))
979 uint32 max_count
= sWorld
.getConfig (CONFIG_UINT32_MAX_OVERSPEED_PINGS
);
981 if (max_count
&& m_OverSpeedPings
> max_count
)
983 ACE_GUARD_RETURN (LockType
, Guard
, m_SessionLock
, -1);
985 if (m_Session
&& m_Session
->GetSecurity () == SEC_PLAYER
)
987 sLog
.outError ("WorldSocket::HandlePing: Player kicked for "
988 "over-speed pings address = %s",
989 GetRemoteAddress ().c_str ());
996 m_OverSpeedPings
= 0;
1001 ACE_GUARD_RETURN (LockType
, Guard
, m_SessionLock
, -1);
1004 m_Session
->SetLatency (latency
);
1007 sLog
.outError ("WorldSocket::HandlePing: peer sent CMSG_PING, "
1008 "but is not authenticated or got recently kicked,"
1010 GetRemoteAddress ().c_str ());
1015 WorldPacket
packet (SMSG_PONG
, 4);
1017 return SendPacket (packet
);