2010-10-07 15:35:36 +02:00
/*
2017-01-01 16:21:30 +01:00
* Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/>
2010-10-07 15:35:36 +02:00
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* 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, see <http://www.gnu.org/licenses/>.
2010-10-07 14:50:05 +02:00
*/
2009-10-17 15:51:44 -07:00
2008-11-02 16:53:46 -06:00
#include "WorldSocket.h"
2014-10-30 02:04:54 +00:00
#include "AuthenticationPackets.h"
2016-03-28 15:23:41 +02:00
#include "BattlenetRpcErrorCodes.h"
2010-06-07 16:21:52 -06:00
#include "BigNumber.h"
2015-02-23 22:35:26 +01:00
#include "CharacterPackets.h"
2016-03-25 21:33:55 +01:00
#include "HmacHash.h"
2014-07-06 01:26:29 +02:00
#include "Opcodes.h"
2014-07-19 13:30:51 +02:00
#include "PacketLog.h"
2016-03-25 21:33:55 +01:00
#include "ScriptMgr.h"
#include "SessionKeyGeneration.h"
#include "SHA256.h"
2014-11-09 00:37:33 +01:00
#include "World.h"
2015-07-25 17:34:41 +02:00
2014-10-31 22:36:43 +01:00
#include <zlib.h>
2014-08-19 19:32:06 +02:00
#include <memory>
2009-10-17 15:51:44 -07:00
2014-10-31 22:36:43 +01:00
#pragma pack(push, 1)
struct CompressedWorldPacket
{
uint32 UncompressedSize ;
uint32 UncompressedAdler ;
uint32 CompressedAdler ;
};
2016-02-19 19:23:04 +01:00
class EncryptablePacket : public WorldPacket
{
public :
EncryptablePacket ( WorldPacket const & packet , bool encrypt ) : WorldPacket ( packet ), _encrypt ( encrypt ) { }
bool NeedsEncryption () const { return _encrypt ; }
private :
bool _encrypt ;
};
2014-10-31 22:36:43 +01:00
#pragma pack(pop)
2014-07-06 01:26:29 +02:00
using boost :: asio :: ip :: tcp ;
2012-07-18 20:14:51 +02:00
2014-08-22 20:06:24 +02:00
std :: string const WorldSocket :: ServerConnectionInitialize ( "WORLD OF WARCRAFT CONNECTION - SERVER TO CLIENT" );
2016-06-13 19:54:06 +02:00
std :: string const WorldSocket :: ClientConnectionInitialize ( "WORLD OF WARCRAFT CONNECTION - CLIENT TO SERVER" );
2015-06-13 00:16:06 +02:00
uint32 const WorldSocket :: MinSizeForCompression = 0x400 ;
2014-08-22 20:06:24 +02:00
2016-06-13 19:54:06 +02:00
uint32 const SizeOfClientHeader = sizeof ( uint32 ) + sizeof ( uint16 );
uint32 const SizeOfServerHeader = sizeof ( uint32 ) + sizeof ( uint16 );
2016-03-10 23:26:26 +01:00
2016-03-25 21:33:55 +01:00
uint8 const WorldSocket :: AuthCheckSeed [ 16 ] = { 0xC5 , 0xC6 , 0x98 , 0x95 , 0x76 , 0x3F , 0x1D , 0xCD , 0xB6 , 0xA1 , 0x37 , 0x28 , 0xB3 , 0x12 , 0xFF , 0x8A };
uint8 const WorldSocket :: SessionKeySeed [ 16 ] = { 0x58 , 0xCB , 0xCF , 0x40 , 0xFE , 0x2E , 0xCE , 0xA6 , 0x5A , 0x90 , 0xB8 , 0x01 , 0x68 , 0x6C , 0x28 , 0x0B };
uint8 const WorldSocket :: ContinuedSessionSeed [ 16 ] = { 0x16 , 0xAD , 0x0C , 0xD4 , 0x46 , 0xF9 , 0x4F , 0xB2 , 0xEF , 0x7D , 0xEA , 0x2A , 0x17 , 0x66 , 0x4D , 0x2F };
2014-11-09 00:37:33 +01:00
WorldSocket :: WorldSocket ( tcp :: socket && socket ) : Socket ( std :: move ( socket )),
2016-06-13 19:54:06 +02:00
_type ( CONNECTION_TYPE_REALM ), _key ( 0 ), _OverSpeedPings ( 0 ),
2016-03-10 23:26:26 +01:00
_worldSession ( nullptr ), _authed ( false ), _compressionStream ( nullptr )
2008-10-02 16:23:55 -05:00
{
2016-03-25 21:33:55 +01:00
_serverChallenge . SetRand ( 8 * 16 );
2016-06-13 19:54:06 +02:00
_headerBuffer . Resize ( SizeOfClientHeader );
2008-10-04 06:17:19 -05:00
}
2009-10-17 15:51:44 -07:00
2014-11-09 00:37:33 +01:00
WorldSocket ::~ WorldSocket ()
{
if ( _compressionStream )
{
deflateEnd ( _compressionStream );
delete _compressionStream ;
}
}
2014-07-07 22:03:41 +02:00
void WorldSocket :: Start ()
2008-10-04 06:17:19 -05:00
{
2015-06-20 00:59:31 +02:00
std :: string ip_address = GetRemoteIpAddress (). to_string ();
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_IP_INFO );
stmt -> setString ( 0 , ip_address );
stmt -> setUInt32 ( 1 , inet_addr ( ip_address . c_str ()));
2016-02-19 19:23:04 +01:00
_queryCallback = std :: bind ( & WorldSocket :: CheckIpCallback , this , std :: placeholders :: _1 );
_queryFuture = LoginDatabase . AsyncQuery ( stmt );
2015-06-20 00:59:31 +02:00
}
void WorldSocket :: CheckIpCallback ( PreparedQueryResult result )
{
if ( result )
{
bool banned = false ;
do
{
Field * fields = result -> Fetch ();
if ( fields [ 0 ]. GetUInt64 () != 0 )
banned = true ;
if ( ! fields [ 1 ]. GetString (). empty ())
_ipCountry = fields [ 1 ]. GetString ();
} while ( result -> NextRow ());
if ( banned )
{
TC_LOG_ERROR ( "network" , "WorldSocket::CheckIpCallback: Sent Auth Response (IP %s banned)." , GetRemoteIpAddress (). to_string (). c_str ());
DelayedCloseSocket ();
return ;
}
}
2016-06-13 19:54:06 +02:00
_packetBuffer . Resize ( ClientConnectionInitialize . length () + 1 );
2016-03-10 23:26:26 +01:00
AsyncReadWithCallback ( & WorldSocket :: InitializeHandler );
2012-01-30 15:28:38 +01:00
2014-09-12 20:26:46 +02:00
MessageBuffer initializer ;
2014-09-12 22:09:07 +02:00
initializer . Write ( ServerConnectionInitialize . c_str (), ServerConnectionInitialize . length ());
2016-06-13 19:54:06 +02:00
initializer . Write ( " \n " , 1 );
2014-08-22 20:06:24 +02:00
2016-02-19 19:23:04 +01:00
// - io_service.run thread, safe.
QueuePacket ( std :: move ( initializer ));
2008-10-04 06:17:19 -05:00
}
2009-10-17 15:51:44 -07:00
2016-03-10 23:26:26 +01:00
void WorldSocket :: InitializeHandler ( boost :: system :: error_code error , std :: size_t transferedBytes )
{
if ( error )
{
CloseSocket ();
return ;
}
GetReadBuffer (). WriteCompleted ( transferedBytes );
MessageBuffer & packet = GetReadBuffer ();
if ( packet . GetActiveSize () > 0 )
{
if ( _packetBuffer . GetRemainingSpace () > 0 )
{
// need to receive the header
std :: size_t readHeaderSize = std :: min ( packet . GetActiveSize (), _packetBuffer . GetRemainingSpace ());
_packetBuffer . Write ( packet . GetReadPointer (), readHeaderSize );
packet . ReadCompleted ( readHeaderSize );
if ( _packetBuffer . GetRemainingSpace () > 0 )
{
// Couldn't receive the whole header this time.
ASSERT ( packet . GetActiveSize () == 0 );
AsyncReadWithCallback ( & WorldSocket :: InitializeHandler );
return ;
}
2016-03-24 21:15:02 +01:00
ByteBuffer buffer ( std :: move ( _packetBuffer ));
2016-06-13 19:54:06 +02:00
std :: string initializer = buffer . ReadString ( ClientConnectionInitialize . length ());
if ( initializer != ClientConnectionInitialize )
2016-04-10 00:08:11 +02:00
{
CloseSocket ();
return ;
}
2016-06-13 19:54:06 +02:00
uint8 terminator ;
buffer >> terminator ;
if ( terminator != '\n' )
2016-03-10 23:26:26 +01:00
{
CloseSocket ();
return ;
}
_compressionStream = new z_stream ();
_compressionStream -> zalloc = ( alloc_func ) NULL ;
_compressionStream -> zfree = ( free_func ) NULL ;
_compressionStream -> opaque = ( voidpf ) NULL ;
_compressionStream -> avail_in = 0 ;
_compressionStream -> next_in = NULL ;
int32 z_res = deflateInit2 ( _compressionStream , sWorld -> getIntConfig ( CONFIG_COMPRESSION ), Z_DEFLATED , - 15 , 8 , Z_DEFAULT_STRATEGY );
if ( z_res != Z_OK )
{
CloseSocket ();
TC_LOG_ERROR ( "network" , "Can't initialize packet compression (zlib: deflateInit) Error code: %i (%s)" , z_res , zError ( z_res ));
return ;
}
_packetBuffer . Reset ();
HandleSendAuthSession ();
AsyncRead ();
return ;
}
}
AsyncReadWithCallback ( & WorldSocket :: InitializeHandler );
}
2015-06-20 00:59:31 +02:00
bool WorldSocket :: Update ()
{
2016-02-19 19:23:04 +01:00
EncryptablePacket * queued ;
MessageBuffer buffer ;
while ( _bufferQueue . Dequeue ( queued ))
{
uint32 packetSize = queued -> size ();
if ( packetSize > MinSizeForCompression && queued -> NeedsEncryption ())
packetSize = compressBound ( packetSize ) + sizeof ( CompressedWorldPacket );
2016-06-13 19:54:06 +02:00
if ( buffer . GetRemainingSpace () < packetSize + SizeOfServerHeader )
2016-02-19 19:23:04 +01:00
{
QueuePacket ( std :: move ( buffer ));
buffer . Resize ( 4096 );
}
2016-06-13 19:54:06 +02:00
if ( buffer . GetRemainingSpace () >= packetSize + SizeOfServerHeader )
2016-02-19 19:23:04 +01:00
WritePacketToBuffer ( * queued , buffer );
else // single packet larger than 4096 bytes
{
2016-06-13 19:54:06 +02:00
MessageBuffer packetBuffer ( packetSize + SizeOfServerHeader );
2016-02-19 19:23:04 +01:00
WritePacketToBuffer ( * queued , packetBuffer );
QueuePacket ( std :: move ( packetBuffer ));
}
delete queued ;
}
if ( buffer . GetActiveSize () > 0 )
QueuePacket ( std :: move ( buffer ));
2015-06-20 00:59:31 +02:00
if ( ! BaseSocket :: Update ())
return false ;
2016-02-19 19:23:04 +01:00
if ( _queryFuture . valid () && _queryFuture . wait_for ( std :: chrono :: seconds ( 0 )) == std :: future_status :: ready )
2015-06-20 00:59:31 +02:00
{
2016-02-19 19:23:04 +01:00
auto callback = _queryCallback ;
_queryCallback = nullptr ;
callback ( _queryFuture . get ());
2015-06-20 00:59:31 +02:00
}
return true ;
}
2014-07-07 22:03:41 +02:00
void WorldSocket :: HandleSendAuthSession ()
2008-10-04 06:17:19 -05:00
{
2014-11-09 00:37:33 +01:00
_encryptSeed . SetRand ( 16 * 8 );
_decryptSeed . SetRand ( 16 * 8 );
2009-10-17 15:51:44 -07:00
2014-10-31 01:20:53 +01:00
WorldPackets :: Auth :: AuthChallenge challenge ;
2016-03-25 21:33:55 +01:00
memcpy ( challenge . Challenge . data (), _serverChallenge . AsByteArray ( 16 ). get (), 16 );
2014-11-09 00:37:33 +01:00
memcpy ( & challenge . DosChallenge [ 0 ], _encryptSeed . AsByteArray ( 16 ). get (), 16 );
memcpy ( & challenge . DosChallenge [ 4 ], _decryptSeed . AsByteArray ( 16 ). get (), 16 );
2014-10-31 01:20:53 +01:00
challenge . DosZeroBits = 1 ;
2015-03-28 21:45:27 +01:00
SendPacketAndLogOpcode ( * challenge . Write ());
}
void WorldSocket :: OnClose ()
{
{
std :: lock_guard < std :: mutex > sessionGuard ( _worldSessionLock );
_worldSession = nullptr ;
}
2014-07-06 01:26:29 +02:00
}
2014-09-09 19:19:25 +02:00
void WorldSocket :: ReadHandler ()
2014-07-06 01:26:29 +02:00
{
2014-09-09 19:19:25 +02:00
if ( ! IsOpen ())
return ;
MessageBuffer & packet = GetReadBuffer ();
while ( packet . GetActiveSize () > 0 )
{
if ( _headerBuffer . GetRemainingSpace () > 0 )
{
// need to receive the header
std :: size_t readHeaderSize = std :: min ( packet . GetActiveSize (), _headerBuffer . GetRemainingSpace ());
_headerBuffer . Write ( packet . GetReadPointer (), readHeaderSize );
packet . ReadCompleted ( readHeaderSize );
if ( _headerBuffer . GetRemainingSpace () > 0 )
{
// Couldn't receive the whole header this time.
ASSERT ( packet . GetActiveSize () == 0 );
break ;
}
// We just received nice new header
if ( ! ReadHeaderHandler ())
2015-04-03 15:38:03 +02:00
{
CloseSocket ();
2014-09-09 19:19:25 +02:00
return ;
2015-04-03 15:38:03 +02:00
}
2014-09-09 19:19:25 +02:00
}
// We have full read header, now check the data payload
if ( _packetBuffer . GetRemainingSpace () > 0 )
{
// need more data in the payload
std :: size_t readDataSize = std :: min ( packet . GetActiveSize (), _packetBuffer . GetRemainingSpace ());
_packetBuffer . Write ( packet . GetReadPointer (), readDataSize );
packet . ReadCompleted ( readDataSize );
if ( _packetBuffer . GetRemainingSpace () > 0 )
{
// Couldn't receive the whole data this time.
ASSERT ( packet . GetActiveSize () == 0 );
break ;
}
}
2014-07-06 15:17:59 +02:00
2014-09-09 19:19:25 +02:00
// just received fresh new payload
2015-08-09 15:30:06 +02:00
ReadDataHandlerResult result = ReadDataHandler ();
_headerBuffer . Reset ();
if ( result != ReadDataHandlerResult :: Ok )
2015-04-03 15:38:03 +02:00
{
2015-08-09 15:30:06 +02:00
if ( result != ReadDataHandlerResult :: WaitingForQuery )
CloseSocket ();
2014-09-09 19:19:25 +02:00
return ;
2015-04-03 15:38:03 +02:00
}
2014-09-09 19:19:25 +02:00
}
AsyncRead ();
2008-10-04 06:17:19 -05:00
}
2009-10-17 15:51:44 -07:00
2015-04-19 15:30:24 +02:00
void WorldSocket :: SetWorldSession ( WorldSession * session )
{
std :: lock_guard < std :: mutex > sessionGuard ( _worldSessionLock );
_worldSession = session ;
_authed = true ;
}
2014-09-09 19:19:25 +02:00
bool WorldSocket :: ReadHeaderHandler ()
2008-10-04 06:17:19 -05:00
{
2016-06-13 19:54:06 +02:00
ASSERT ( _headerBuffer . GetActiveSize () == SizeOfClientHeader , "Header size " SZFMTD " different than expected %u" , _headerBuffer . GetActiveSize (), SizeOfClientHeader );
2014-09-09 19:19:25 +02:00
2016-03-24 21:15:02 +01:00
_authCrypt . DecryptRecv ( _headerBuffer . GetReadPointer (), 4 );
2009-10-17 15:51:44 -07:00
2016-06-13 19:54:06 +02:00
PacketHeader * header = reinterpret_cast < PacketHeader *> ( _headerBuffer . GetReadPointer ());
header -> Size -= 2 ;
2014-09-12 22:09:07 +02:00
2016-06-13 19:54:06 +02:00
if ( ! header -> IsValidSize () || ! header -> IsValidOpcode ())
2014-08-19 19:32:06 +02:00
{
2015-03-28 21:45:27 +01:00
TC_LOG_ERROR ( "network" , "WorldSocket::ReadHeaderHandler(): client %s sent malformed packet (size: %u, cmd: %u)" ,
2016-06-13 19:54:06 +02:00
GetRemoteIpAddress (). to_string (). c_str (), header -> Size , header -> Command );
2014-09-09 19:19:25 +02:00
return false ;
2014-08-19 19:32:06 +02:00
}
2016-06-13 19:54:06 +02:00
_packetBuffer . Resize ( header -> Size );
2014-09-09 19:19:25 +02:00
return true ;
2008-10-04 06:17:19 -05:00
}
2009-10-17 15:51:44 -07:00
2015-08-09 15:30:06 +02:00
WorldSocket :: ReadDataHandlerResult WorldSocket :: ReadDataHandler ()
2008-10-04 06:17:19 -05:00
{
2016-06-13 19:54:06 +02:00
PacketHeader * header = reinterpret_cast < PacketHeader *> ( _headerBuffer . GetReadPointer ());
OpcodeClient opcode = static_cast < OpcodeClient > ( header -> Command );
2009-10-17 15:51:44 -07:00
2016-03-10 23:26:26 +01:00
WorldPacket packet ( opcode , std :: move ( _packetBuffer ), GetConnectionType ());
2009-10-17 15:51:44 -07:00
2016-03-10 23:26:26 +01:00
if ( sPacketLog -> CanLogPacket ())
sPacketLog -> LogPacket ( packet , CLIENT_TO_SERVER , GetRemoteIpAddress (), GetRemotePort (), GetConnectionType ());
2014-07-19 13:30:51 +02:00
2016-03-10 23:26:26 +01:00
std :: unique_lock < std :: mutex > sessionGuard ( _worldSessionLock , std :: defer_lock );
2014-07-19 13:30:51 +02:00
2016-03-10 23:26:26 +01:00
switch ( opcode )
{
case CMSG_PING :
2016-07-07 18:12:50 +02:00
{
2016-03-10 23:26:26 +01:00
LogOpcodeText ( opcode , sessionGuard );
2016-07-07 18:12:50 +02:00
WorldPackets :: Auth :: Ping ping ( std :: move ( packet ));
if ( ! ping . ReadNoThrow ())
{
TC_LOG_ERROR ( "network" , "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_PING" , GetRemoteIpAddress (). to_string (). c_str ());
return ReadDataHandlerResult :: Error ;
}
if ( ! HandlePing ( ping ))
return ReadDataHandlerResult :: Error ;
break ;
}
2016-03-10 23:26:26 +01:00
case CMSG_AUTH_SESSION :
2014-08-22 20:06:24 +02:00
{
2016-03-10 23:26:26 +01:00
LogOpcodeText ( opcode , sessionGuard );
if ( _authed )
2014-11-09 00:37:33 +01:00
{
2016-03-10 23:26:26 +01:00
// locking just to safely log offending user is probably overkill but we are disconnecting him anyway
if ( sessionGuard . try_lock ())
TC_LOG_ERROR ( "network" , "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s" , _worldSession -> GetPlayerInfo (). c_str ());
return ReadDataHandlerResult :: Error ;
2014-11-09 00:37:33 +01:00
}
2015-03-28 21:45:27 +01:00
2016-03-10 23:26:26 +01:00
std :: shared_ptr < WorldPackets :: Auth :: AuthSession > authSession = std :: make_shared < WorldPackets :: Auth :: AuthSession > ( std :: move ( packet ));
2016-07-07 18:12:50 +02:00
if ( ! authSession -> ReadNoThrow ())
{
TC_LOG_ERROR ( "network" , "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_AUTH_SESSION" , GetRemoteIpAddress (). to_string (). c_str ());
return ReadDataHandlerResult :: Error ;
}
2016-03-25 21:33:55 +01:00
HandleAuthSession ( authSession );
return ReadDataHandlerResult :: WaitingForQuery ;
2016-03-10 23:26:26 +01:00
}
case CMSG_AUTH_CONTINUED_SESSION :
{
LogOpcodeText ( opcode , sessionGuard );
if ( _authed )
2014-07-27 12:09:32 +02:00
{
2016-03-10 23:26:26 +01:00
// locking just to safely log offending user is probably overkill but we are disconnecting him anyway
if ( sessionGuard . try_lock ())
TC_LOG_ERROR ( "network" , "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_CONTINUED_SESSION from %s" , _worldSession -> GetPlayerInfo (). c_str ());
return ReadDataHandlerResult :: Error ;
}
2015-03-28 21:45:27 +01:00
2016-03-10 23:26:26 +01:00
std :: shared_ptr < WorldPackets :: Auth :: AuthContinuedSession > authSession = std :: make_shared < WorldPackets :: Auth :: AuthContinuedSession > ( std :: move ( packet ));
2016-07-07 18:12:50 +02:00
if ( ! authSession -> ReadNoThrow ())
{
TC_LOG_ERROR ( "network" , "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_AUTH_CONTINUED_SESSION" , GetRemoteIpAddress (). to_string (). c_str ());
return ReadDataHandlerResult :: Error ;
}
2016-03-25 21:33:55 +01:00
HandleAuthContinuedSession ( authSession );
return ReadDataHandlerResult :: WaitingForQuery ;
2016-03-10 23:26:26 +01:00
}
case CMSG_KEEP_ALIVE :
LogOpcodeText ( opcode , sessionGuard );
break ;
case CMSG_LOG_DISCONNECT :
LogOpcodeText ( opcode , sessionGuard );
packet . rfinish (); // contains uint32 disconnectReason;
break ;
case CMSG_ENABLE_NAGLE :
LogOpcodeText ( opcode , sessionGuard );
SetNoDelay ( false );
break ;
case CMSG_CONNECT_TO_FAILED :
{
sessionGuard . lock ();
2015-03-28 21:45:27 +01:00
2016-03-10 23:26:26 +01:00
LogOpcodeText ( opcode , sessionGuard );
WorldPackets :: Auth :: ConnectToFailed connectToFailed ( std :: move ( packet ));
2016-07-07 18:12:50 +02:00
if ( ! connectToFailed . ReadNoThrow ())
{
TC_LOG_ERROR ( "network" , "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_CONNECT_TO_FAILED" , GetRemoteIpAddress (). to_string (). c_str ());
return ReadDataHandlerResult :: Error ;
}
2016-03-10 23:26:26 +01:00
HandleConnectToFailed ( connectToFailed );
break ;
}
2016-06-13 19:54:06 +02:00
case CMSG_ENABLE_ENCRYPTION_ACK :
LogOpcodeText ( opcode , sessionGuard );
HandleEnableEncryptionAck ();
break ;
2016-03-10 23:26:26 +01:00
default :
{
sessionGuard . lock ();
2014-08-22 20:06:24 +02:00
2016-03-10 23:26:26 +01:00
LogOpcodeText ( opcode , sessionGuard );
2014-10-12 12:38:04 +02:00
2016-03-10 23:26:26 +01:00
if ( ! _worldSession )
{
TC_LOG_ERROR ( "network.opcode" , "ProcessIncoming: Client not authed opcode = %u" , uint32 ( opcode ));
return ReadDataHandlerResult :: Error ;
}
2014-08-22 20:06:24 +02:00
2016-03-10 23:26:26 +01:00
OpcodeHandler const * handler = opcodeTable [ opcode ];
if ( ! handler )
{
TC_LOG_ERROR ( "network.opcode" , "No defined handler for opcode %s sent by %s" , GetOpcodeNameForLogging ( static_cast < OpcodeClient > ( packet . GetOpcode ())). c_str (), _worldSession -> GetPlayerInfo (). c_str ());
2014-07-27 12:09:32 +02:00
break ;
}
2014-11-09 00:37:33 +01:00
2016-03-10 23:26:26 +01:00
// Our Idle timer will reset on any non PING opcodes.
// Catches people idling on the login screen and any lingering ingame connections.
_worldSession -> ResetTimeOutTime ();
// Copy the packet to the heap before enqueuing
_worldSession -> QueuePacket ( new WorldPacket ( std :: move ( packet )));
break ;
}
2014-07-26 23:26:01 +02:00
}
2014-08-07 19:02:08 +02:00
2015-08-09 15:30:06 +02:00
return ReadDataHandlerResult :: Ok ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2015-03-28 21:50:58 +01:00
void WorldSocket :: LogOpcodeText ( OpcodeClient opcode , std :: unique_lock < std :: mutex > const & guard ) const
2015-03-28 21:45:27 +01:00
{
if ( ! guard )
{
TC_LOG_TRACE ( "network.opcode" , "C->S: %s %s" , GetRemoteIpAddress (). to_string (). c_str (), GetOpcodeNameForLogging ( opcode ). c_str ());
}
else
{
TC_LOG_TRACE ( "network.opcode" , "C->S: %s %s" , ( _worldSession ? _worldSession -> GetPlayerInfo () : GetRemoteIpAddress (). to_string ()). c_str (),
GetOpcodeNameForLogging ( opcode ). c_str ());
}
}
void WorldSocket :: SendPacketAndLogOpcode ( WorldPacket const & packet )
{
TC_LOG_TRACE ( "network.opcode" , "S->C: %s %s" , GetRemoteIpAddress (). to_string (). c_str (), GetOpcodeNameForLogging ( static_cast < OpcodeServer > ( packet . GetOpcode ())). c_str ());
SendPacket ( packet );
}
2014-11-09 00:37:33 +01:00
void WorldSocket :: SendPacket ( WorldPacket const & packet )
{
if ( ! IsOpen ())
return ;
if ( sPacketLog -> CanLogPacket ())
2014-11-09 20:48:13 +01:00
sPacketLog -> LogPacket ( packet , SERVER_TO_CLIENT , GetRemoteIpAddress (), GetRemotePort (), GetConnectionType ());
2014-11-09 00:37:33 +01:00
2016-02-19 19:23:04 +01:00
_bufferQueue . Enqueue ( new EncryptablePacket ( packet , _authCrypt . IsInitialized ()));
2014-11-09 00:37:33 +01:00
}
2016-02-19 19:23:04 +01:00
void WorldSocket :: WritePacketToBuffer ( EncryptablePacket const & packet , MessageBuffer & buffer )
2008-10-02 16:23:55 -05:00
{
2014-10-31 22:36:43 +01:00
uint32 opcode = packet . GetOpcode ();
uint32 packetSize = packet . size ();
2014-08-11 20:43:07 +02:00
2014-10-31 22:36:43 +01:00
// Reserve space for buffer
uint8 * headerPos = buffer . GetWritePointer ();
2016-06-13 19:54:06 +02:00
buffer . WriteCompleted ( SizeOfServerHeader );
2014-07-19 13:30:51 +02:00
2016-02-19 19:23:04 +01:00
if ( packetSize > MinSizeForCompression && packet . NeedsEncryption ())
2014-10-31 22:36:43 +01:00
{
CompressedWorldPacket cmp ;
2016-03-24 21:15:02 +01:00
cmp . UncompressedSize = packetSize + 2 ;
cmp . UncompressedAdler = adler32 ( adler32 ( 0x9827D8F1 , ( Bytef * ) & opcode , 2 ), packet . contents (), packetSize );
2009-10-17 15:51:44 -07:00
2014-10-31 22:36:43 +01:00
// Reserve space for compression info - uncompressed size and checksums
uint8 * compressionInfo = buffer . GetWritePointer ();
buffer . WriteCompleted ( sizeof ( CompressedWorldPacket ));
2009-10-17 15:51:44 -07:00
2014-11-09 00:37:33 +01:00
uint32 compressedSize = CompressPacket ( buffer . GetWritePointer (), packet );
2014-10-31 22:36:43 +01:00
cmp . CompressedAdler = adler32 ( 0x9827D8F1 , buffer . GetWritePointer (), compressedSize );
memcpy ( compressionInfo , & cmp , sizeof ( CompressedWorldPacket ));
buffer . WriteCompleted ( compressedSize );
packetSize = compressedSize + sizeof ( CompressedWorldPacket );
opcode = SMSG_COMPRESSED_PACKET ;
}
else if ( ! packet . empty ())
buffer . Write ( packet . contents (), packet . size ());
2009-10-17 15:51:44 -07:00
2016-03-24 21:15:02 +01:00
packetSize += 2 /*opcode*/ ;
2016-06-13 19:54:06 +02:00
PacketHeader header ;
header . Size = packetSize ;
header . Command = opcode ;
_authCrypt . EncryptSend (( uint8 * ) & header , 4 );
2014-07-24 17:08:53 +02:00
2016-06-13 19:54:06 +02:00
memcpy ( headerPos , & header , SizeOfServerHeader );
2014-10-31 22:36:43 +01:00
}
2014-11-09 00:37:33 +01:00
uint32 WorldSocket :: CompressPacket ( uint8 * buffer , WorldPacket const & packet )
2014-10-31 22:36:43 +01:00
{
2014-11-09 00:37:33 +01:00
uint32 opcode = packet . GetOpcode ();
2016-03-24 21:15:02 +01:00
uint32 bufferSize = deflateBound ( _compressionStream , packet . size () + sizeof ( uint16 ));
2014-10-31 22:36:43 +01:00
2014-11-09 00:37:33 +01:00
_compressionStream -> next_out = buffer ;
_compressionStream -> avail_out = bufferSize ;
_compressionStream -> next_in = ( Bytef * ) & opcode ;
2016-03-24 21:15:02 +01:00
_compressionStream -> avail_in = sizeof ( uint16 );
2014-10-31 22:36:43 +01:00
2015-06-13 00:16:06 +02:00
int32 z_res = deflate ( _compressionStream , Z_NO_FLUSH );
2014-11-09 00:37:33 +01:00
if ( z_res != Z_OK )
{
TC_LOG_ERROR ( "network" , "Can't compress packet opcode (zlib: deflate) Error code: %i (%s, msg: %s)" , z_res , zError ( z_res ), _compressionStream -> msg );
return 0 ;
}
2014-10-31 22:36:43 +01:00
2014-11-09 00:37:33 +01:00
_compressionStream -> next_in = ( Bytef * ) packet . contents ();
_compressionStream -> avail_in = packet . size ();
2014-10-31 22:36:43 +01:00
2014-11-09 00:37:33 +01:00
z_res = deflate ( _compressionStream , Z_SYNC_FLUSH );
if ( z_res != Z_OK )
2014-09-09 19:19:25 +02:00
{
2014-11-09 00:37:33 +01:00
TC_LOG_ERROR ( "network" , "Can't compress packet data (zlib: deflate) Error code: %i (%s, msg: %s)" , z_res , zError ( z_res ), _compressionStream -> msg );
return 0 ;
2014-09-09 19:19:25 +02:00
}
2014-11-09 00:37:33 +01:00
return bufferSize - _compressionStream -> avail_out ;
2014-07-24 17:08:53 +02:00
}
2015-06-20 00:59:31 +02:00
struct AccountInfo
2008-10-02 16:23:55 -05:00
{
2015-06-20 00:59:31 +02:00
struct
{
uint32 Id ;
bool IsLockedToIP ;
std :: string LastIP ;
2015-11-02 20:23:39 +01:00
std :: string LockCountry ;
2015-06-20 00:59:31 +02:00
LocaleConstant Locale ;
bool IsBanned ;
} BattleNet ;
2012-07-04 18:24:05 +02:00
2015-06-20 00:59:31 +02:00
struct
{
uint32 Id ;
BigNumber SessionKey ;
uint8 Expansion ;
int64 MuteTime ;
uint32 Recruiter ;
2016-03-25 21:33:55 +01:00
std :: string OS ;
2015-06-20 00:59:31 +02:00
bool IsRectuiter ;
AccountTypes Security ;
bool IsBanned ;
} Game ;
bool IsBanned () const { return BattleNet . IsBanned || Game . IsBanned ; }
explicit AccountInfo ( Field * fields )
{
2016-03-25 21:33:55 +01:00
// 0 1 2 3 4 5 6 7 8 9 10 11
// SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, ba.locale, a.recruiter, a.os, ba.id, aa.gmLevel,
2015-11-02 20:23:39 +01:00
// 12 13 14
2015-06-20 00:59:31 +02:00
// bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id
// FROM account a LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID IN (-1, ?)
// LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account_banned ab ON a.id = ab.id LEFT JOIN account r ON a.id = r.recruiter
// WHERE a.username = ? ORDER BY aa.RealmID DESC LIMIT 1
Game . Id = fields [ 0 ]. GetUInt32 ();
Game . SessionKey . SetHexStr ( fields [ 1 ]. GetCString ());
BattleNet . LastIP = fields [ 2 ]. GetString ();
BattleNet . IsLockedToIP = fields [ 3 ]. GetBool ();
2015-11-02 20:23:39 +01:00
BattleNet . LockCountry = fields [ 4 ]. GetString ();
Game . Expansion = fields [ 5 ]. GetUInt8 ();
Game . MuteTime = fields [ 6 ]. GetInt64 ();
BattleNet . Locale = LocaleConstant ( fields [ 7 ]. GetUInt8 ());
Game . Recruiter = fields [ 8 ]. GetUInt32 ();
2016-03-25 21:33:55 +01:00
Game . OS = fields [ 9 ]. GetString ();
2015-11-02 20:23:39 +01:00
BattleNet . Id = fields [ 10 ]. GetUInt32 ();
Game . Security = AccountTypes ( fields [ 11 ]. GetUInt8 ());
BattleNet . IsBanned = fields [ 12 ]. GetUInt64 () != 0 ;
Game . IsBanned = fields [ 13 ]. GetUInt64 () != 0 ;
Game . IsRectuiter = fields [ 14 ]. GetUInt32 () != 0 ;
2015-06-20 00:59:31 +02:00
uint32 world_expansion = sWorld -> getIntConfig ( CONFIG_EXPANSION );
if ( Game . Expansion > world_expansion )
Game . Expansion = world_expansion ;
if ( BattleNet . Locale >= TOTAL_LOCALES )
BattleNet . Locale = LOCALE_enUS ;
}
};
void WorldSocket :: HandleAuthSession ( std :: shared_ptr < WorldPackets :: Auth :: AuthSession > authSession )
{
2014-07-29 23:45:34 +02:00
// Get the account information from the auth database
2014-08-22 20:09:18 +02:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_ACCOUNT_INFO_BY_NAME );
2015-10-06 00:30:47 +02:00
stmt -> setInt32 ( 0 , int32 ( realm . Id . Realm ));
2016-03-25 21:33:55 +01:00
stmt -> setString ( 1 , authSession -> RealmJoinTicket );
2012-05-30 08:01:02 +02:00
2016-02-19 19:23:04 +01:00
_queryCallback = std :: bind ( & WorldSocket :: HandleAuthSessionCallback , this , authSession , std :: placeholders :: _1 );
_queryFuture = LoginDatabase . AsyncQuery ( stmt );
2015-06-20 00:59:31 +02:00
}
2009-10-17 15:51:44 -07:00
2015-06-20 00:59:31 +02:00
void WorldSocket :: HandleAuthSessionCallback ( std :: shared_ptr < WorldPackets :: Auth :: AuthSession > authSession , PreparedQueryResult result )
{
2008-11-06 16:10:28 -06:00
// Stop if the account is not found
if ( ! result )
2008-10-04 06:17:19 -05:00
{
2014-05-02 03:44:21 +02:00
// We can not log here, as we do not know the account. Thus, no accountId.
2013-11-08 10:50:51 +01:00
TC_LOG_ERROR ( "network" , "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account)." );
2014-08-21 18:18:13 +02:00
DelayedCloseSocket ();
2014-07-05 19:41:18 -05:00
return ;
2008-10-04 06:17:19 -05:00
}
2009-10-17 15:51:44 -07:00
2015-06-20 00:59:31 +02:00
AccountInfo account ( result -> Fetch ());
2009-10-17 15:51:44 -07:00
2014-05-02 03:44:21 +02:00
// For hook purposes, we get Remoteaddress at this point.
2014-07-26 23:26:01 +02:00
std :: string address = GetRemoteIpAddress (). to_string ();
2014-05-02 03:44:21 +02:00
2016-03-25 21:33:55 +01:00
HmacSha256 hmac ( SHA256_DIGEST_LENGTH , account . Game . SessionKey . AsByteArray ( SHA256_DIGEST_LENGTH ). get ());
hmac . UpdateData ( authSession -> LocalChallenge . data (), authSession -> LocalChallenge . size ());
hmac . UpdateData ( _serverChallenge . AsByteArray ( 16 ). get (), 16 );
hmac . UpdateData ( AuthCheckSeed , 16 );
hmac . Finalize ();
2016-03-28 15:23:41 +02:00
// Check that Key and account name are the same on client and server
2016-03-25 21:33:55 +01:00
if ( memcmp ( hmac . GetDigest (), authSession -> Digest . data (), authSession -> Digest . size ()) != 0 )
{
TC_LOG_ERROR ( "network" , "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s" , account . Game . Id , authSession -> RealmJoinTicket . c_str (), address . c_str ());
DelayedCloseSocket ();
return ;
}
HmacSha256 sessionKeyHmac ( SHA256_DIGEST_LENGTH , account . Game . SessionKey . AsByteArray ( SHA256_DIGEST_LENGTH ). get ());
sessionKeyHmac . UpdateData ( _serverChallenge . AsByteArray ( 16 ). get (), 16 );
sessionKeyHmac . UpdateData ( authSession -> LocalChallenge . data (), authSession -> LocalChallenge . size ());
sessionKeyHmac . UpdateData ( SessionKeySeed , 16 );
sessionKeyHmac . Finalize ();
uint8 sessionKey [ 40 ];
SessionKeyGenerator < SHA256Hash > sessionKeyGenerator ( sessionKeyHmac . GetDigest (), sessionKeyHmac . GetLength ());
sessionKeyGenerator . Generate ( sessionKey , 40 );
2016-06-13 19:54:06 +02:00
_sessionKey . SetBinary ( sessionKey , 40 );
2016-03-25 21:33:55 +01:00
2014-05-02 03:44:21 +02:00
// As we don't know if attempted login process by ip works, we update last_attempt_ip right away
2015-06-20 00:59:31 +02:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_LAST_ATTEMPT_IP );
2014-05-02 03:44:21 +02:00
stmt -> setString ( 0 , address );
2016-03-25 21:33:55 +01:00
stmt -> setString ( 1 , authSession -> RealmJoinTicket );
2014-05-02 03:44:21 +02:00
LoginDatabase . Execute ( stmt );
// This also allows to check for possible "hack" attempts on account
2016-03-28 15:23:41 +02:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_ACCOUNT_INFO_CONTINUED_SESSION );
2016-06-13 19:54:06 +02:00
stmt -> setString ( 0 , _sessionKey . AsHexStr ());
2016-03-28 15:23:41 +02:00
stmt -> setUInt32 ( 1 , account . Game . Id );
LoginDatabase . Execute ( stmt );
2014-08-21 18:18:13 +02:00
// First reject the connection if packet contains invalid data or realm state doesn't allow logging in
if ( sWorld -> IsClosed ())
{
2016-03-28 15:23:41 +02:00
SendAuthResponseError ( ERROR_DENIED );
2014-08-21 18:18:13 +02:00
TC_LOG_ERROR ( "network" , "WorldSocket::HandleAuthSession: World closed, denying client (%s)." , GetRemoteIpAddress (). to_string (). c_str ());
DelayedCloseSocket ();
return ;
}
2015-10-06 00:30:47 +02:00
if ( authSession -> RealmID != realm . Id . Realm )
2014-08-21 18:18:13 +02:00
{
2016-03-28 15:23:41 +02:00
SendAuthResponseError ( ERROR_DENIED );
2016-01-20 17:19:42 +01:00
TC_LOG_ERROR ( "network" , "WorldSocket::HandleAuthSession: Client %s requested connecting with realm id %u but this realm has id %u set in config." ,
GetRemoteIpAddress (). to_string (). c_str (), authSession -> RealmID , realm . Id . Realm );
2014-08-21 18:18:13 +02:00
DelayedCloseSocket ();
return ;
}
// Must be done before WorldSession is created
2015-06-20 00:59:31 +02:00
bool wardenActive = sWorld -> getBoolConfig ( CONFIG_WARDEN_ENABLED );
2016-03-25 21:33:55 +01:00
if ( wardenActive && account . Game . OS != "Win" && account . Game . OS != "Wn64" && account . Game . OS != "Mc64" )
2015-06-20 00:59:31 +02:00
{
2016-03-28 15:23:41 +02:00
SendAuthResponseError ( ERROR_DENIED );
2016-03-25 21:33:55 +01:00
TC_LOG_ERROR ( "network" , "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s)." , address . c_str (), account . Game . OS . c_str ());
2014-08-21 18:18:13 +02:00
DelayedCloseSocket ();
return ;
}
2014-07-29 23:45:34 +02:00
///- Re-check ip locking (same check as in auth).
2015-06-20 00:59:31 +02:00
if ( account . BattleNet . IsLockedToIP )
{
if ( account . BattleNet . LastIP != address )
{
2016-03-28 15:23:41 +02:00
SendAuthResponseError ( ERROR_DENIED );
2015-06-20 00:59:31 +02:00
TC_LOG_DEBUG ( "network" , "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s)." , account . BattleNet . LastIP . c_str (), address . c_str ());
// We could log on hook only instead of an additional db log, however action logger is config based. Better keep DB logging as well
sScriptMgr -> OnFailedAccountLogin ( account . Game . Id );
DelayedCloseSocket ();
return ;
}
}
2015-11-02 20:23:39 +01:00
else if ( ! account . BattleNet . LockCountry . empty () && account . BattleNet . LockCountry != "00" && ! _ipCountry . empty ())
2008-10-04 06:17:19 -05:00
{
2015-06-20 00:59:31 +02:00
if ( account . BattleNet . LockCountry != _ipCountry )
2008-10-04 06:17:19 -05:00
{
2016-03-28 15:23:41 +02:00
SendAuthResponseError ( ERROR_DENIED );
2015-06-20 00:59:31 +02:00
TC_LOG_DEBUG ( "network" , "WorldSocket::HandleAuthSession: Sent Auth Response (Account country differs. Original country: %s, new country: %s)." , account . BattleNet . LockCountry . c_str (), _ipCountry . c_str ());
2014-05-02 03:44:21 +02:00
// We could log on hook only instead of an additional db log, however action logger is config based. Better keep DB logging as well
2015-06-20 00:59:31 +02:00
sScriptMgr -> OnFailedAccountLogin ( account . Game . Id );
2014-08-21 18:18:13 +02:00
DelayedCloseSocket ();
2014-07-05 19:41:18 -05:00
return ;
2008-10-04 06:17:19 -05:00
}
}
2009-10-17 15:51:44 -07:00
2015-06-20 00:59:31 +02:00
int64 mutetime = account . Game . MuteTime ;
2011-07-27 17:51:57 +02:00
//! Negative mutetime indicates amount of seconds to be muted effective on next login - which is now.
if ( mutetime < 0 )
{
2011-07-28 23:44:39 +02:00
mutetime = time ( NULL ) + llabs ( mutetime );
2011-12-27 00:29:17 +01:00
2014-08-22 20:09:18 +02:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_MUTE_TIME_LOGIN );
2011-12-27 00:29:17 +01:00
stmt -> setInt64 ( 0 , mutetime );
2015-06-20 00:59:31 +02:00
stmt -> setUInt32 ( 1 , account . Game . Id );
2011-12-27 00:29:17 +01:00
LoginDatabase . Execute ( stmt );
2011-07-27 17:51:57 +02:00
}
2009-10-17 15:51:44 -07:00
2015-06-20 00:59:31 +02:00
if ( account . IsBanned ())
2008-10-04 06:17:19 -05:00
{
2016-03-28 15:23:41 +02:00
SendAuthResponseError ( ERROR_DENIED );
2013-11-08 10:50:51 +01:00
TC_LOG_ERROR ( "network" , "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)." );
2015-06-20 00:59:31 +02:00
sScriptMgr -> OnFailedAccountLogin ( account . Game . Id );
2014-08-21 18:18:13 +02:00
DelayedCloseSocket ();
2014-07-05 19:41:18 -05:00
return ;
2008-10-06 04:14:44 -05:00
}
2009-10-17 15:51:44 -07:00
2008-11-06 16:10:28 -06:00
// Check locked state for server
2010-12-23 23:25:44 +01:00
AccountTypes allowedAccountType = sWorld -> GetPlayerSecurityLimit ();
2015-06-20 00:59:31 +02:00
TC_LOG_DEBUG ( "network" , "Allowed Level: %u Player Level %u" , allowedAccountType , account . Game . Security );
if ( allowedAccountType > SEC_PLAYER && account . Game . Security < allowedAccountType )
2008-10-06 04:14:44 -05:00
{
2016-03-28 15:23:41 +02:00
SendAuthResponseError ( ERROR_DENIED );
2015-03-08 05:51:07 +01:00
TC_LOG_DEBUG ( "network" , "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough" );
2015-06-20 00:59:31 +02:00
sScriptMgr -> OnFailedAccountLogin ( account . Game . Id );
2014-08-21 18:18:13 +02:00
DelayedCloseSocket ();
2014-07-05 19:41:18 -05:00
return ;
2012-07-04 18:24:05 +02:00
}
2016-03-25 21:33:55 +01:00
TC_LOG_DEBUG ( "network" , "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s." , authSession -> RealmJoinTicket . c_str (), address . c_str ());
2011-08-03 09:28:12 -07:00
2014-05-02 03:44:21 +02:00
// Update the last_ip in the database as it was successful for login
2012-05-30 08:01:02 +02:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_LAST_IP );
2011-12-27 00:29:17 +01:00
stmt -> setString ( 0 , address );
2016-03-25 21:33:55 +01:00
stmt -> setString ( 1 , authSession -> RealmJoinTicket );
2011-12-27 00:29:17 +01:00
2011-12-27 16:31:10 +01:00
LoginDatabase . Execute ( stmt );
2009-10-17 15:51:44 -07:00
2014-08-21 18:18:13 +02:00
// At this point, we can safely hook a successful login
2015-06-20 00:59:31 +02:00
sScriptMgr -> OnAccountLogin ( account . Game . Id );
2009-10-17 15:51:44 -07:00
2015-03-28 21:45:27 +01:00
_authed = true ;
2016-03-25 21:33:55 +01:00
_worldSession = new WorldSession ( account . Game . Id , std :: move ( authSession -> RealmJoinTicket ), account . BattleNet . Id , shared_from_this (), account . Game . Security ,
2016-03-28 15:23:41 +02:00
account . Game . Expansion , mutetime , account . Game . OS , account . BattleNet . Locale , account . Game . Recruiter , account . Game . IsRectuiter );
2009-10-17 15:51:44 -07:00
2012-02-19 13:51:16 +01:00
// Initialize Warden system only if it is enabled by config
2014-05-02 03:44:21 +02:00
if ( wardenActive )
2016-06-13 19:54:06 +02:00
_worldSession -> InitWarden ( & _sessionKey );
2015-06-20 00:59:31 +02:00
2016-02-19 19:23:04 +01:00
_queryCallback = std :: bind ( & WorldSocket :: LoadSessionPermissionsCallback , this , std :: placeholders :: _1 );
2015-06-20 00:59:31 +02:00
_queryFuture = _worldSession -> LoadPermissionsAsync ();
2015-08-09 15:30:06 +02:00
AsyncRead ();
2015-06-20 00:59:31 +02:00
}
void WorldSocket :: LoadSessionPermissionsCallback ( PreparedQueryResult result )
{
// RBAC must be loaded before adding session to check for skip queue permission
_worldSession -> GetRBACData () -> LoadFromDBCallback ( result );
2012-02-19 13:51:16 +01:00
2016-06-13 19:54:06 +02:00
SendPacketAndLogOpcode ( * WorldPackets :: Auth :: EnableEncryption (). Write ());
2014-07-06 01:26:29 +02:00
}
2009-10-17 15:51:44 -07:00
2015-06-20 00:59:31 +02:00
void WorldSocket :: HandleAuthContinuedSession ( std :: shared_ptr < WorldPackets :: Auth :: AuthContinuedSession > authSession )
2014-11-09 00:37:33 +01:00
{
2015-12-29 21:22:31 +01:00
WorldSession :: ConnectToKey key ;
key . Raw = authSession -> Key ;
_type = ConnectionType ( key . Fields . ConnectionType );
2015-06-20 00:59:31 +02:00
if ( _type != CONNECTION_TYPE_INSTANCE )
{
2016-03-28 15:23:41 +02:00
SendAuthResponseError ( ERROR_DENIED );
2015-06-20 00:59:31 +02:00
DelayedCloseSocket ();
return ;
}
2014-11-09 21:36:06 +01:00
2015-12-29 21:22:31 +01:00
uint32 accountId = uint32 ( key . Fields . AccountId );
2014-11-09 21:36:06 +01:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_ACCOUNT_INFO_CONTINUED_SESSION );
stmt -> setUInt32 ( 0 , accountId );
2015-06-20 00:59:31 +02:00
2016-02-19 19:23:04 +01:00
_queryCallback = std :: bind ( & WorldSocket :: HandleAuthContinuedSessionCallback , this , authSession , std :: placeholders :: _1 );
_queryFuture = LoginDatabase . AsyncQuery ( stmt );
2015-06-20 00:59:31 +02:00
}
void WorldSocket :: HandleAuthContinuedSessionCallback ( std :: shared_ptr < WorldPackets :: Auth :: AuthContinuedSession > authSession , PreparedQueryResult result )
{
2014-11-09 00:37:33 +01:00
if ( ! result )
{
2016-03-28 15:23:41 +02:00
SendAuthResponseError ( ERROR_DENIED );
2014-11-09 00:37:33 +01:00
DelayedCloseSocket ();
return ;
}
2015-12-29 21:22:31 +01:00
WorldSession :: ConnectToKey key ;
2016-06-13 19:54:06 +02:00
_key = key . Raw = authSession -> Key ;
2015-12-29 21:22:31 +01:00
uint32 accountId = uint32 ( key . Fields . AccountId );
2014-11-09 00:37:33 +01:00
Field * fields = result -> Fetch ();
std :: string login = fields [ 0 ]. GetString ();
2016-06-13 19:54:06 +02:00
_sessionKey . SetHexStr ( fields [ 1 ]. GetCString ());
2014-11-09 00:37:33 +01:00
2016-06-13 19:54:06 +02:00
HmacSha256 hmac ( 40 , _sessionKey . AsByteArray ( 40 ). get ());
2016-03-25 21:33:55 +01:00
hmac . UpdateData ( reinterpret_cast < uint8 const *> ( & authSession -> Key ), sizeof ( authSession -> Key ));
hmac . UpdateData ( authSession -> LocalChallenge . data (), authSession -> LocalChallenge . size ());
hmac . UpdateData ( _serverChallenge . AsByteArray ( 16 ). get (), 16 );
hmac . UpdateData ( ContinuedSessionSeed , 16 );
hmac . Finalize ();
2014-11-09 00:37:33 +01:00
2016-03-25 21:33:55 +01:00
if ( memcmp ( hmac . GetDigest (), authSession -> Digest . data (), authSession -> Digest . size ()))
2014-11-09 00:37:33 +01:00
{
TC_LOG_ERROR ( "network" , "WorldSocket::HandleAuthContinuedSession: Authentication failed for account: %u ('%s') address: %s" , accountId , login . c_str (), GetRemoteIpAddress (). to_string (). c_str ());
DelayedCloseSocket ();
return ;
}
2016-06-13 19:54:06 +02:00
SendPacketAndLogOpcode ( * WorldPackets :: Auth :: EnableEncryption (). Write ());
2015-08-09 15:30:06 +02:00
AsyncRead ();
2014-11-09 00:37:33 +01:00
}
2015-02-23 22:35:26 +01:00
void WorldSocket :: HandleConnectToFailed ( WorldPackets :: Auth :: ConnectToFailed & connectToFailed )
{
if ( _worldSession )
{
if ( _worldSession -> PlayerLoading ())
{
switch ( connectToFailed . Serial )
{
case WorldPackets :: Auth :: ConnectToSerial :: WorldAttempt1 :
_worldSession -> SendConnectToInstance ( WorldPackets :: Auth :: ConnectToSerial :: WorldAttempt2 );
break ;
case WorldPackets :: Auth :: ConnectToSerial :: WorldAttempt2 :
_worldSession -> SendConnectToInstance ( WorldPackets :: Auth :: ConnectToSerial :: WorldAttempt3 );
break ;
case WorldPackets :: Auth :: ConnectToSerial :: WorldAttempt3 :
_worldSession -> SendConnectToInstance ( WorldPackets :: Auth :: ConnectToSerial :: WorldAttempt4 );
break ;
case WorldPackets :: Auth :: ConnectToSerial :: WorldAttempt4 :
_worldSession -> SendConnectToInstance ( WorldPackets :: Auth :: ConnectToSerial :: WorldAttempt5 );
break ;
case WorldPackets :: Auth :: ConnectToSerial :: WorldAttempt5 :
{
TC_LOG_ERROR ( "network" , "%s failed to connect 5 times to world socket, aborting login" , _worldSession -> GetPlayerInfo (). c_str ());
2015-02-24 00:32:01 +01:00
_worldSession -> AbortLogin ( WorldPackets :: Character :: LoginFailureReason :: NoWorld );
2015-02-23 22:35:26 +01:00
break ;
}
default :
return ;
}
}
//else
//{
// transfer_aborted when/if we get map node redirection
2015-03-28 21:45:27 +01:00
// SendPacketAndLogOpcode(*WorldPackets::Auth::ResumeComms().Write());
2015-02-23 22:35:26 +01:00
//}
}
}
2016-06-13 19:54:06 +02:00
void WorldSocket :: HandleEnableEncryptionAck ()
{
if ( _type == CONNECTION_TYPE_REALM )
{
_authCrypt . Init ( & _sessionKey );
sWorld -> AddSession ( _worldSession );
}
else
{
_authCrypt . Init ( & _sessionKey , _encryptSeed . AsByteArray (). get (), _decryptSeed . AsByteArray (). get ());
sWorld -> AddInstanceSocket ( shared_from_this (), _key );
}
}
2016-03-28 15:23:41 +02:00
void WorldSocket :: SendAuthResponseError ( uint32 code )
2014-07-06 01:26:29 +02:00
{
2014-10-30 02:04:54 +00:00
WorldPackets :: Auth :: AuthResponse response ;
response . Result = code ;
2015-03-28 21:45:27 +01:00
SendPacketAndLogOpcode ( * response . Write ());
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2016-07-07 18:12:50 +02:00
bool WorldSocket :: HandlePing ( WorldPackets :: Auth :: Ping & ping )
2008-10-02 16:23:55 -05:00
{
2015-07-21 20:42:07 +02:00
using namespace std :: chrono ;
2014-07-06 18:35:04 +02:00
if ( _LastPingTime == steady_clock :: time_point ())
{
_LastPingTime = steady_clock :: now ();
}
2008-11-06 16:10:28 -06:00
else
2008-10-04 06:17:19 -05:00
{
2014-07-06 18:35:04 +02:00
steady_clock :: time_point now = steady_clock :: now ();
steady_clock :: duration diff = now - _LastPingTime ;
_LastPingTime = now ;
2009-10-17 15:51:44 -07:00
2014-07-06 18:35:04 +02:00
if ( diff < seconds ( 27 ))
2008-10-04 06:17:19 -05:00
{
2014-07-06 18:35:04 +02:00
++ _OverSpeedPings ;
2009-10-17 15:51:44 -07:00
2014-07-06 18:35:04 +02:00
uint32 maxAllowed = sWorld -> getIntConfig ( CONFIG_MAX_OVERSPEED_PINGS );
2009-10-17 15:51:44 -07:00
2014-07-06 18:35:04 +02:00
if ( maxAllowed && _OverSpeedPings > maxAllowed )
2008-10-04 06:17:19 -05:00
{
2015-04-01 01:47:40 +02:00
std :: unique_lock < std :: mutex > sessionGuard ( _worldSessionLock );
2015-03-28 21:45:27 +01:00
2014-07-06 18:35:04 +02:00
if ( _worldSession && ! _worldSession -> HasPermission ( rbac :: RBAC_PERM_SKIP_CHECK_OVERSPEED_PING ))
2008-10-04 06:17:19 -05:00
{
2013-11-08 10:50:51 +01:00
TC_LOG_ERROR ( "network" , "WorldSocket::HandlePing: %s kicked for over-speed pings (address: %s)" ,
2014-07-26 23:26:01 +02:00
_worldSession -> GetPlayerInfo (). c_str (), GetRemoteIpAddress (). to_string (). c_str ());
2009-10-17 15:51:44 -07:00
2015-04-03 15:38:03 +02:00
return false ;
2008-10-04 06:17:19 -05:00
}
}
}
2008-11-06 16:10:28 -06:00
else
2014-07-06 18:35:04 +02:00
_OverSpeedPings = 0 ;
2008-10-04 06:17:19 -05:00
}
2009-10-17 15:51:44 -07:00
2014-07-06 18:35:04 +02:00
{
2015-03-28 21:45:27 +01:00
std :: lock_guard < std :: mutex > sessionGuard ( _worldSessionLock );
2009-10-17 15:51:44 -07:00
2015-03-28 21:45:27 +01:00
if ( _worldSession )
{
2016-07-07 18:12:50 +02:00
_worldSession -> SetLatency ( ping . Latency );
2015-03-28 21:45:27 +01:00
_worldSession -> ResetClientTimeDelay ();
}
else
{
TC_LOG_ERROR ( "network" , "WorldSocket::HandlePing: peer sent CMSG_PING, but is not authenticated or got recently kicked, address = %s" , GetRemoteIpAddress (). to_string (). c_str ());
2015-04-03 15:38:03 +02:00
return false ;
2015-03-28 21:45:27 +01:00
}
2008-11-06 16:10:28 -06:00
}
2009-10-17 15:51:44 -07:00
2016-07-07 18:12:50 +02:00
SendPacketAndLogOpcode ( * WorldPackets :: Auth :: Pong ( ping . Serial ). Write ());
2015-04-03 15:38:03 +02:00
return true ;
2014-08-11 17:28:10 +02:00
}