2 * Copyright (C) 2005-2008 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"
38 #include "AddonHandler.h"
40 #include "Database/DatabaseEnv.h"
41 #include "Auth/Sha1.h"
42 #include "WorldSession.h"
43 #include "WorldSocketMgr.h"
47 #if defined( __GNUC__ )
53 struct ServerPktHeader
59 struct ClientPktHeader
65 #if defined( __GNUC__ )
71 WorldSocket::WorldSocket (void) :
76 m_Header (sizeof (ClientPktHeader
)),
78 m_OutBufferSize (65536),
80 m_Seed (static_cast<uint32
> (rand32 ())),
82 m_LastPingTime (ACE_Time_Value::zero
)
84 reference_counting_policy ().value (ACE_Event_Handler::Reference_Counting_Policy::ENABLED
);
87 WorldSocket::~WorldSocket (void)
93 m_OutBuffer
->release ();
100 while (m_PacketQueue
.dequeue_head (pct
) == 0)
104 bool WorldSocket::IsClosed (void) const
109 void WorldSocket::CloseSocket (void)
112 ACE_GUARD (LockType
, Guard
, m_OutBufferLock
);
119 peer ().close_writer ();
123 ACE_GUARD (LockType
, Guard
, m_SessionLock
);
129 const std::string
& WorldSocket::GetRemoteAddress (void) const
134 int WorldSocket::SendPacket (const WorldPacket
& pct
)
136 ACE_GUARD_RETURN (LockType
, Guard
, m_OutBufferLock
, -1);
141 // Dump outgoing packet.
142 if (sWorldLog
.LogWorld ())
144 sWorldLog
.Log ("SERVER:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n",
145 (uint32
) get_handle (),
147 LookupOpcodeName (pct
.GetOpcode ()),
151 while (p
< pct
.size ())
153 for (uint32 j
= 0; j
< 16 && p
< pct
.size (); j
++)
154 sWorldLog
.Log ("%.2X ", const_cast<WorldPacket
&>(pct
)[p
++]);
156 sWorldLog
.Log ("\n");
159 sWorldLog
.Log ("\n\n");
162 if (iSendPacket (pct
) == -1)
166 ACE_NEW_RETURN (npct
, WorldPacket (pct
), -1);
168 // NOTE maybe check of the size of the queue can be good ?
169 // to make it bounded instead of unbounded
170 if (m_PacketQueue
.enqueue_tail (npct
) == -1)
173 sLog
.outError ("WorldSocket::SendPacket: m_PacketQueue.enqueue_tail failed");
181 long WorldSocket::AddReference (void)
183 return static_cast<long> (add_reference ());
186 long WorldSocket::RemoveReference (void)
188 return static_cast<long> (remove_reference ());
191 int WorldSocket::open (void *a
)
195 // Prevent double call to this func.
199 // This will also prevent the socket from being Updated
200 // while we are initializing it.
203 // Hook for the manager.
204 if (sWorldSocketMgr
->OnSocketOpen (this) == -1)
207 // Allocate the buffer.
208 ACE_NEW_RETURN (m_OutBuffer
, ACE_Message_Block (m_OutBufferSize
), -1);
210 // Store peer address.
211 ACE_INET_Addr remote_addr
;
213 if (peer ().get_remote_addr (remote_addr
) == -1)
215 sLog
.outError ("WorldSocket::open: peer ().get_remote_addr errno = %s", ACE_OS::strerror (errno
));
219 m_Address
= remote_addr
.get_host_addr ();
221 // Send startup packet.
222 WorldPacket
packet (SMSG_AUTH_CHALLENGE
, 4);
225 if (SendPacket (packet
) == -1)
228 // Register with ACE Reactor
229 if (reactor ()->register_handler(this, ACE_Event_Handler::READ_MASK
| ACE_Event_Handler::WRITE_MASK
) == -1)
231 sLog
.outError ("WorldSocket::open: unable to register client handler errno = %s", ACE_OS::strerror (errno
));
235 // reactor takes care of the socket from now on
241 int WorldSocket::close (int)
252 int WorldSocket::handle_input (ACE_HANDLE
)
257 switch (handle_input_missing_data ())
261 if ((errno
== EWOULDBLOCK
) ||
264 return Update (); // interesting line ,isn't it ?
267 DEBUG_LOG ("WorldSocket::handle_input: Peer error closing connection errno = %s", ACE_OS::strerror (errno
));
274 DEBUG_LOG ("WorldSocket::handle_input: Peer has closed connection\n");
282 return Update (); // another interesting line ;)
285 ACE_NOTREACHED(return -1);
288 int WorldSocket::handle_output (ACE_HANDLE
)
290 ACE_GUARD_RETURN (LockType
, Guard
, m_OutBufferLock
, -1);
295 const size_t send_len
= m_OutBuffer
->length ();
298 return cancel_wakeup_output (Guard
);
301 ssize_t n
= peer ().send (m_OutBuffer
->rd_ptr (), send_len
, MSG_NOSIGNAL
);
303 ssize_t n
= peer ().send (m_OutBuffer
->rd_ptr (), send_len
);
304 #endif // MSG_NOSIGNAL
310 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
311 return schedule_wakeup_output (Guard
);
315 else if (n
< send_len
) //now n > 0
317 m_OutBuffer
->rd_ptr (static_cast<size_t> (n
));
319 // move the data to the base of the buffer
320 m_OutBuffer
->crunch ();
322 return schedule_wakeup_output (Guard
);
324 else //now n == send_len
326 m_OutBuffer
->reset ();
328 if (!iFlushPacketQueue ())
329 return cancel_wakeup_output (Guard
);
331 return schedule_wakeup_output (Guard
);
334 ACE_NOTREACHED (return 0);
337 int WorldSocket::handle_close (ACE_HANDLE h
, ACE_Reactor_Mask
)
341 ACE_GUARD_RETURN (LockType
, Guard
, m_OutBufferLock
, -1);
345 if (h
== ACE_INVALID_HANDLE
)
346 peer ().close_writer ();
351 ACE_GUARD_RETURN (LockType
, Guard
, m_SessionLock
, -1);
359 int WorldSocket::Update (void)
364 if (m_OutActive
|| m_OutBuffer
->length () == 0)
367 return handle_output (get_handle ());
370 int WorldSocket::handle_input_header (void)
372 ACE_ASSERT (m_RecvWPct
== NULL
);
374 ACE_ASSERT (m_Header
.length () == sizeof (ClientPktHeader
));
376 m_Crypt
.DecryptRecv ((ACE_UINT8
*) m_Header
.rd_ptr (), sizeof (ClientPktHeader
));
378 ClientPktHeader
& header
= *((ClientPktHeader
*) m_Header
.rd_ptr ());
380 EndianConvertReverse(header
.size
);
381 EndianConvert(header
.cmd
);
383 if ((header
.size
< 4) || (header
.size
> 10240) ||
384 (header
.cmd
< 0) || (header
.cmd
> 10240) )
386 sLog
.outError ("WorldSocket::handle_input_header: client sent mailformed packet size = %d , cmd = %d",
387 header
.size
, header
.cmd
);
395 ACE_NEW_RETURN (m_RecvWPct
, WorldPacket ((uint16
) header
.cmd
, header
.size
), -1);
399 m_RecvWPct
->resize (header
.size
);
400 m_RecvPct
.base ((char*) m_RecvWPct
->contents (), m_RecvWPct
->size ());
404 ACE_ASSERT(m_RecvPct
.space() == 0);
410 int WorldSocket::handle_input_payload (void)
412 // set errno properly here on error !!!
413 // now have a header and payload
415 ACE_ASSERT (m_RecvPct
.space () == 0);
416 ACE_ASSERT (m_Header
.space () == 0);
417 ACE_ASSERT (m_RecvWPct
!= NULL
);
419 const int ret
= ProcessIncoming (m_RecvWPct
);
421 m_RecvPct
.base (NULL
, 0);
433 int WorldSocket::handle_input_missing_data (void)
437 ACE_Data_Block
db ( sizeof (buf
),
438 ACE_Message_Block::MB_DATA
,
442 ACE_Message_Block::DONT_DELETE
,
445 ACE_Message_Block
message_block(&db
,
446 ACE_Message_Block::DONT_DELETE
,
449 const size_t recv_size
= message_block
.space ();
451 const ssize_t n
= peer ().recv (message_block
.wr_ptr (),
457 message_block
.wr_ptr (n
);
459 while (message_block
.length () > 0)
461 if (m_Header
.space () > 0)
463 //need to receive the header
464 const size_t to_header
= (message_block
.length () > m_Header
.space () ? m_Header
.space () : message_block
.length ());
465 m_Header
.copy (message_block
.rd_ptr (), to_header
);
466 message_block
.rd_ptr (to_header
);
468 if (m_Header
.space () > 0)
470 // Couldn't receive the whole header this time.
471 ACE_ASSERT (message_block
.length () == 0);
476 // We just received nice new header
477 if (handle_input_header () == -1)
479 ACE_ASSERT ((errno
!= EWOULDBLOCK
) && (errno
!= EAGAIN
));
484 // Its possible on some error situations that this happens
485 // for example on closing when epoll receives more chunked data and stuff
486 // hope this is not hack ,as proper m_RecvWPct is asserted around
489 sLog
.outError ("Forcing close on input m_RecvWPct = NULL");
494 // We have full read header, now check the data payload
495 if (m_RecvPct
.space () > 0)
497 //need more data in the payload
498 const size_t to_data
= (message_block
.length () > m_RecvPct
.space () ? m_RecvPct
.space () : message_block
.length ());
499 m_RecvPct
.copy (message_block
.rd_ptr (), to_data
);
500 message_block
.rd_ptr (to_data
);
502 if (m_RecvPct
.space () > 0)
504 // Couldn't receive the whole data this time.
505 ACE_ASSERT (message_block
.length () == 0);
511 //just received fresh new payload
512 if (handle_input_payload () == -1)
514 ACE_ASSERT ((errno
!= EWOULDBLOCK
) && (errno
!= EAGAIN
));
519 return n
== recv_size
? 1 : 2;
522 int WorldSocket::cancel_wakeup_output (GuardType
& g
)
531 if (reactor ()->cancel_wakeup
532 (this, ACE_Event_Handler::WRITE_MASK
) == -1)
534 // would be good to store errno from reactor with errno guard
535 sLog
.outError ("WorldSocket::cancel_wakeup_output");
542 int WorldSocket::schedule_wakeup_output (GuardType
& g
)
551 if (reactor ()->schedule_wakeup
552 (this, ACE_Event_Handler::WRITE_MASK
) == -1)
554 sLog
.outError ("WorldSocket::schedule_wakeup_output");
561 int WorldSocket::ProcessIncoming (WorldPacket
* new_pct
)
563 ACE_ASSERT (new_pct
);
566 ACE_Auto_Ptr
<WorldPacket
> aptr (new_pct
);
568 const ACE_UINT16 opcode
= new_pct
->GetOpcode ();
573 // Dump received packet.
574 if (sWorldLog
.LogWorld ())
576 sWorldLog
.Log ("CLIENT:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n",
577 (uint32
) get_handle (),
579 LookupOpcodeName (new_pct
->GetOpcode ()),
580 new_pct
->GetOpcode ());
583 while (p
< new_pct
->size ())
585 for (uint32 j
= 0; j
< 16 && p
< new_pct
->size (); j
++)
586 sWorldLog
.Log ("%.2X ", (*new_pct
)[p
++]);
587 sWorldLog
.Log ("\n");
589 sWorldLog
.Log ("\n\n");
592 // like one switch ;)
593 if (opcode
== CMSG_PING
)
595 return HandlePing (*new_pct
);
597 else if (opcode
== CMSG_AUTH_SESSION
)
601 sLog
.outError ("WorldSocket::ProcessIncoming: Player send CMSG_AUTH_SESSION again");
605 return HandleAuthSession (*new_pct
);
607 else if (opcode
== CMSG_KEEP_ALIVE
)
609 DEBUG_LOG ("CMSG_KEEP_ALIVE ,size: %d", new_pct
->size ());
615 ACE_GUARD_RETURN (LockType
, Guard
, m_SessionLock
, -1);
617 if (m_Session
!= NULL
)
619 // OK ,give the packet to WorldSession
621 // WARNINIG here we call it with locks held.
622 // Its possible to cause deadlock if QueuePacket calls back
623 m_Session
->QueuePacket (new_pct
);
628 sLog
.outError ("WorldSocket::ProcessIncoming: Client not authed opcode = ", opcode
);
633 ACE_NOTREACHED (return 0);
636 int WorldSocket::HandleAuthSession (WorldPacket
& recvPacket
)
638 // NOTE: ATM the socket is singlethread, have this in mind ...
642 uint32 BuiltNumberClient
;
645 LocaleConstant locale
;
648 BigNumber v
, s
, g
, N
, x
, I
;
649 WorldPacket packet
, SendAddonPacked
;
653 if (recvPacket
.size () < (4 + 4 + 1 + 4 + 20))
655 sLog
.outError ("WorldSocket::HandleAuthSession: wrong packet size");
659 // Read the content of the packet
660 recvPacket
>> BuiltNumberClient
; // for now no use
662 recvPacket
>> account
;
664 if (recvPacket
.size () < (4 + 4 + (account
.size () + 1) + 4 + 20))
666 sLog
.outError ("WorldSocket::HandleAuthSession: wrong packet size second check");
670 recvPacket
>> clientSeed
;
671 recvPacket
.read (digest
, 20);
673 DEBUG_LOG ("WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, clientseed %u",
679 // Get the account information from the realmd database
680 std::string safe_account
= account
; // Duplicate, else will screw the SHA hash verification below
681 loginDatabase
.escape_string (safe_account
);
682 // No SQL injection, username escaped.
684 QueryResult
*result
=
685 loginDatabase
.PQuery ("SELECT "
691 "sha_pass_hash, " //5
698 "WHERE username = '%s'",
699 safe_account
.c_str ());
701 // Stop if the account is not found
704 packet
.Initialize (SMSG_AUTH_RESPONSE
, 1);
705 packet
<< uint8 (AUTH_UNKNOWN_ACCOUNT
);
709 sLog
.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (unknown account).");
713 Field
* fields
= result
->Fetch ();
715 expansion
= ((sWorld
.getConfig(CONFIG_EXPANSION
) > fields
[8].GetUInt8()) ? fields
[8].GetUInt8() : sWorld
.getConfig(CONFIG_EXPANSION
));
717 N
.SetHexStr ("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
719 I
.SetHexStr (fields
[5].GetString ());
721 //In case of leading zeros in the I hash, restore them
722 uint8 mDigest
[SHA_DIGEST_LENGTH
];
723 memset (mDigest
, 0, SHA_DIGEST_LENGTH
);
725 if (I
.GetNumBytes () <= SHA_DIGEST_LENGTH
)
726 memcpy (mDigest
, I
.AsByteArray (), I
.GetNumBytes ());
728 std::reverse (mDigest
, mDigest
+ SHA_DIGEST_LENGTH
);
730 s
.SetHexStr (fields
[7].GetString ());
731 sha1
.UpdateData (s
.AsByteArray (), s
.GetNumBytes ());
732 sha1
.UpdateData (mDigest
, SHA_DIGEST_LENGTH
);
734 x
.SetBinary (sha1
.GetDigest (), sha1
.GetLength ());
737 const char* sStr
= s
.AsHexStr (); //Must be freed by OPENSSL_free()
738 const char* vStr
= v
.AsHexStr (); //Must be freed by OPENSSL_free()
739 const char* vold
= fields
[6].GetString ();
741 DEBUG_LOG ("WorldSocket::HandleAuthSession: (s,v) check s: %s v_old: %s v_new: %s",
746 loginDatabase
.PExecute ("UPDATE account "
750 "WHERE username = '%s'",
751 safe_account
.c_str ());
753 if (!vold
|| strcmp (vStr
, vold
))
755 packet
.Initialize (SMSG_AUTH_RESPONSE
, 1);
756 packet
<< uint8 (AUTH_UNKNOWN_ACCOUNT
);
759 OPENSSL_free ((void*) sStr
);
760 OPENSSL_free ((void*) vStr
);
762 sLog
.outBasic ("WorldSocket::HandleAuthSession: User not logged.");
766 OPENSSL_free ((void*) sStr
);
767 OPENSSL_free ((void*) vStr
);
769 ///- Re-check ip locking (same check as in realmd).
770 if (fields
[4].GetUInt8 () == 1) // if ip is locked
772 if (strcmp (fields
[3].GetString (), GetRemoteAddress ().c_str ()))
774 packet
.Initialize (SMSG_AUTH_RESPONSE
, 1);
775 packet
<< uint8 (AUTH_FAILED
);
779 sLog
.outBasic ("WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs).");
784 id
= fields
[0].GetUInt32 ();
785 security
= fields
[1].GetUInt16 ();
786 K
.SetHexStr (fields
[2].GetString ());
788 time_t mutetime
= time_t (fields
[9].GetUInt64 ());
790 locale
= LocaleConstant (fields
[10].GetUInt8 ());
791 if (locale
>= MAX_LOCALE
)
792 locale
= LOCALE_enUS
;
796 // Re-check account ban (same check as in realmd)
797 QueryResult
*banresult
=
798 loginDatabase
.PQuery ("SELECT "
801 "FROM account_banned "
806 if (banresult
) // if account banned
808 packet
.Initialize (SMSG_AUTH_RESPONSE
, 1);
809 packet
<< uint8 (AUTH_BANNED
);
814 sLog
.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (Account banned).");
818 // Check locked state for server
819 AccountTypes allowedAccountType
= sWorld
.GetPlayerSecurityLimit ();
821 if (allowedAccountType
> SEC_PLAYER
&& security
< allowedAccountType
)
823 WorldPacket
Packet (SMSG_AUTH_RESPONSE
, 1);
824 Packet
<< uint8 (AUTH_UNAVAILABLE
);
828 sLog
.outBasic ("WorldSocket::HandleAuthSession: User tries to login but his security level is not enough");
832 // Check that Key and account name are the same on client and server
836 uint32 seed
= m_Seed
;
838 sha
.UpdateData (account
);
839 sha
.UpdateData ((uint8
*) & t
, 4);
840 sha
.UpdateData ((uint8
*) & clientSeed
, 4);
841 sha
.UpdateData ((uint8
*) & seed
, 4);
842 sha
.UpdateBigNumbers (&K
, NULL
);
845 if (memcmp (sha
.GetDigest (), digest
, 20))
847 packet
.Initialize (SMSG_AUTH_RESPONSE
, 1);
848 packet
<< uint8 (AUTH_FAILED
);
852 sLog
.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (authentification failed).");
856 std::string address
= GetRemoteAddress ();
858 DEBUG_LOG ("WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.",
862 // Update the last_ip in the database
863 // No SQL injection, username escaped.
864 loginDatabase
.escape_string (address
);
866 loginDatabase
.PExecute ("UPDATE account "
867 "SET last_ip = '%s' "
868 "WHERE username = '%s'",
870 safe_account
.c_str ());
872 // NOTE ATM the socket is singlethreaded, have this in mind ...
873 ACE_NEW_RETURN (m_Session
, WorldSession (id
, this, security
, expansion
, mutetime
, locale
), -1);
878 // In case needed sometime the second arg is in microseconds 1 000 000 = 1 sec
879 ACE_OS::sleep (ACE_Time_Value (0, 10000));
881 sWorld
.AddSession (m_Session
);
883 // Create and send the Addon packet
884 if (sAddOnHandler
.BuildAddonPacket (&recvPacket
, &SendAddonPacked
))
885 SendPacket (SendAddonPacked
);
890 int WorldSocket::HandlePing (WorldPacket
& recvPacket
)
895 if (recvPacket
.size () < 8)
897 sLog
.outError ("WorldSocket::_HandlePing wrong packet size");
901 // Get the ping packet content
903 recvPacket
>> latency
;
905 if (m_LastPingTime
== ACE_Time_Value::zero
)
906 m_LastPingTime
= ACE_OS::gettimeofday (); // for 1st ping
909 ACE_Time_Value cur_time
= ACE_OS::gettimeofday ();
910 ACE_Time_Value
diff_time (cur_time
);
911 diff_time
-= m_LastPingTime
;
912 m_LastPingTime
= cur_time
;
914 if (diff_time
< ACE_Time_Value (27))
918 uint32 max_count
= sWorld
.getConfig (CONFIG_MAX_OVERSPEED_PINGS
);
920 if (max_count
&& m_OverSpeedPings
> max_count
)
922 ACE_GUARD_RETURN (LockType
, Guard
, m_SessionLock
, -1);
924 if (m_Session
&& m_Session
->GetSecurity () == SEC_PLAYER
)
926 sLog
.outError ("WorldSocket::HandlePing: Player kicked for "
927 "over-speed pings address = %s",
928 GetRemoteAddress ().c_str ());
935 m_OverSpeedPings
= 0;
940 ACE_GUARD_RETURN (LockType
, Guard
, m_SessionLock
, -1);
943 m_Session
->SetLatency (latency
);
946 sLog
.outError ("WorldSocket::HandlePing: peer sent CMSG_PING, "
947 "but is not authenticated or got recently kicked,"
949 GetRemoteAddress ().c_str ());
954 WorldPacket
packet (SMSG_PONG
, 4);
956 return SendPacket (packet
);
959 int WorldSocket::iSendPacket (const WorldPacket
& pct
)
961 if (m_OutBuffer
->space () < pct
.size () + sizeof (ServerPktHeader
))
967 ServerPktHeader header
;
969 header
.cmd
= pct
.GetOpcode ();
970 EndianConvert(header
.cmd
);
972 header
.size
= (uint16
) pct
.size () + 2;
973 EndianConvertReverse(header
.size
);
975 m_Crypt
.EncryptSend ((uint8
*) & header
, sizeof (header
));
977 if (m_OutBuffer
->copy ((char*) & header
, sizeof (header
)) == -1)
981 if (m_OutBuffer
->copy ((char*) pct
.contents (), pct
.size ()) == -1)
987 bool WorldSocket::iFlushPacketQueue ()
990 bool haveone
= false;
992 while (m_PacketQueue
.dequeue_head (pct
) == 0)
994 if (iSendPacket (*pct
) == -1)
996 if (m_PacketQueue
.enqueue_head (pct
) == -1)
999 sLog
.outError ("WorldSocket::iFlushPacketQueue m_PacketQueue->enqueue_head");