2010-10-07 15:35:36 +02:00
/*
2015-01-01 00:27:46 +01:00
* Copyright (C) 2008-2015 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"
2010-06-07 16:21:52 -06:00
#include "BigNumber.h"
2015-02-23 22:35:26 +01:00
#include "CharacterPackets.h"
2014-07-06 01:26:29 +02:00
#include "Opcodes.h"
2014-08-19 19:32:06 +02:00
#include "Player.h"
2010-08-06 19:23:43 +02:00
#include "ScriptMgr.h"
2014-07-06 01:26:29 +02:00
#include "SHA1.h"
2014-07-19 13:30:51 +02:00
#include "PacketLog.h"
2014-06-13 17:56:44 +02:00
#include "BattlenetAccountMgr.h"
2014-11-09 00:37:33 +01:00
#include "World.h"
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 ;
};
#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" );
std :: string const WorldSocket :: ClientConnectionInitialize ( "WORLD OF WARCRAFT CONNECTION - CLIENT TO SERVER" );
2014-10-23 10:28:46 +02:00
uint32 const SizeOfClientHeader [ 2 ][ 2 ] =
2014-10-20 01:27:00 +02:00
{
{ 2 , 0 },
{ 6 , 4 }
};
2014-10-23 10:28:46 +02:00
uint32 const SizeOfServerHeader [ 2 ] = { sizeof ( uint16 ) + sizeof ( uint32 ), sizeof ( uint32 ) };
2014-09-12 20:26:46 +02:00
2014-11-09 00:37:33 +01:00
WorldSocket :: WorldSocket ( tcp :: socket && socket ) : Socket ( std :: move ( socket )),
2014-12-07 01:24:00 +01:00
_type ( CONNECTION_TYPE_REALM ), _authSeed ( rand32 ()), _OverSpeedPings ( 0 ),
_worldSession ( nullptr ), _compressionStream ( nullptr ), _initialized ( false )
2008-10-02 16:23:55 -05:00
{
2014-10-23 10:32:14 +02:00
_headerBuffer . Resize ( SizeOfClientHeader [ 0 ][ 0 ]);
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
{
2014-09-09 19:19:25 +02:00
AsyncRead ();
2012-01-30 15:28:38 +01:00
2014-09-12 20:26:46 +02:00
MessageBuffer initializer ;
2014-10-20 01:27:00 +02:00
ServerPktHeader header ;
header . Setup . Size = ServerConnectionInitialize . size ();
initializer . Write ( & header , sizeof ( header . Setup . Size ));
2014-09-12 22:09:07 +02:00
initializer . Write ( ServerConnectionInitialize . c_str (), ServerConnectionInitialize . length ());
2014-08-22 20:06:24 +02:00
2014-09-12 20:26:46 +02:00
std :: unique_lock < std :: mutex > dummy ( _writeLock , std :: defer_lock );
QueuePacket ( std :: move ( initializer ), dummy );
2008-10-04 06:17:19 -05:00
}
2009-10-17 15:51:44 -07:00
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 ;
challenge . Challenge = _authSeed ;
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 ;
2014-11-03 20:23:21 +01:00
SendPacket ( * challenge . Write ());
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 ())
return ;
}
// 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
if ( ! ReadDataHandler ())
return ;
_headerBuffer . Reset ();
}
AsyncRead ();
2008-10-04 06:17:19 -05:00
}
2009-10-17 15:51:44 -07:00
2014-10-20 01:27:00 +02:00
void WorldSocket :: ExtractOpcodeAndSize ( ClientPktHeader const * header , uint32 & opcode , uint32 & size ) const
{
if ( _authCrypt . IsInitialized ())
{
opcode = header -> Normal . Command ;
size = header -> Normal . Size ;
}
else
{
opcode = header -> Setup . Command ;
size = header -> Setup . Size ;
if ( _initialized )
size -= 4 ;
}
}
2014-09-09 19:19:25 +02:00
bool WorldSocket :: ReadHeaderHandler ()
2008-10-04 06:17:19 -05:00
{
2014-10-27 21:56:12 +00:00
ASSERT ( _headerBuffer . GetActiveSize () == SizeOfClientHeader [ _initialized ][ _authCrypt . IsInitialized ()], "Header size " SZFMTD " different than expected %u" , _headerBuffer . GetActiveSize (), SizeOfClientHeader [ _initialized ][ _authCrypt . IsInitialized ()]);
2014-09-09 19:19:25 +02:00
2014-09-12 22:09:07 +02:00
_authCrypt . DecryptRecv ( _headerBuffer . GetReadPointer (), _headerBuffer . GetActiveSize ());
2009-10-17 15:51:44 -07:00
2014-09-09 19:19:25 +02:00
ClientPktHeader * header = reinterpret_cast < ClientPktHeader *> ( _headerBuffer . GetReadPointer ());
2014-10-20 01:27:00 +02:00
uint32 opcode ;
uint32 size ;
2009-10-17 15:51:44 -07:00
2014-10-20 01:27:00 +02:00
ExtractOpcodeAndSize ( header , opcode , size );
2014-09-12 22:09:07 +02:00
2014-10-20 01:27:00 +02:00
if ( ! ClientPktHeader :: IsValidSize ( size ) || ( _initialized && ! ClientPktHeader :: IsValidOpcode ( opcode )))
2014-08-19 19:32:06 +02:00
{
if ( _worldSession )
{
Player * player = _worldSession -> GetPlayer ();
2014-11-10 09:26:01 +01:00
TC_LOG_ERROR ( "network" , "WorldSocket::ReadHeaderHandler(): client (account: %u, char [%s, name: %s]) sent malformed packet (size: %u, cmd: %u)" ,
2014-10-22 17:33:55 +02:00
_worldSession -> GetAccountId (), player ? player -> GetGUID (). ToString (). c_str () : "GUID: Empty" , player ? player -> GetName (). c_str () : "<none>" , size , opcode );
2014-08-19 19:32:06 +02:00
}
else
2014-11-10 09:26:01 +01:00
TC_LOG_ERROR ( "network" , "WorldSocket::ReadHeaderHandler() : client % s sent malformed packet ( size : % u , cmd : % u ) ",
2014-10-20 01:27:00 +02:00
GetRemoteIpAddress (). to_string (). c_str (), size , opcode );
2014-08-19 19:32:06 +02:00
CloseSocket ();
2014-09-09 19:19:25 +02:00
return false ;
2014-08-19 19:32:06 +02:00
}
2014-10-20 01:27:00 +02:00
_packetBuffer . Resize ( 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
2014-09-09 19:19:25 +02:00
bool WorldSocket :: ReadDataHandler ()
2008-10-04 06:17:19 -05:00
{
2014-08-22 20:06:24 +02:00
if ( _initialized )
{
2014-09-12 20:26:46 +02:00
ClientPktHeader * header = reinterpret_cast < ClientPktHeader *> ( _headerBuffer . GetReadPointer ());
2014-10-20 01:27:00 +02:00
uint32 cmd ;
uint32 size ;
2009-10-17 15:51:44 -07:00
2014-10-20 01:27:00 +02:00
ExtractOpcodeAndSize ( header , cmd , size );
2014-10-25 15:41:42 +01:00
OpcodeClient opcode = static_cast < OpcodeClient > ( cmd );
2009-10-17 15:51:44 -07:00
2014-08-22 20:06:24 +02:00
std :: string opcodeName = GetOpcodeNameForLogging ( opcode );
2009-10-17 15:51:44 -07:00
2014-11-09 00:37:33 +01:00
WorldPacket packet ( opcode , std :: move ( _packetBuffer ), GetConnectionType ());
2009-10-17 15:51:44 -07:00
2014-08-22 20:06:24 +02:00
if ( sPacketLog -> CanLogPacket ())
2014-11-09 20:48:13 +01:00
sPacketLog -> LogPacket ( packet , CLIENT_TO_SERVER , GetRemoteIpAddress (), GetRemotePort (), GetConnectionType ());
2014-07-19 13:30:51 +02:00
2014-08-22 20:06:24 +02:00
TC_LOG_TRACE ( "network.opcode" , "C->S: %s %s" , ( _worldSession ? _worldSession -> GetPlayerInfo () : GetRemoteIpAddress (). to_string ()). c_str (), opcodeName . c_str ());
2014-07-19 13:30:51 +02:00
2014-08-22 20:06:24 +02:00
switch ( opcode )
{
case CMSG_PING :
HandlePing ( packet );
break ;
case CMSG_AUTH_SESSION :
2014-11-09 00:37:33 +01:00
{
2014-08-22 20:06:24 +02:00
if ( _worldSession )
{
TC_LOG_ERROR ( "network" , "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s" , _worldSession -> GetPlayerInfo (). c_str ());
break ;
}
2014-11-09 00:37:33 +01:00
WorldPackets :: Auth :: AuthSession authSession ( std :: move ( packet ));
authSession . Read ();
HandleAuthSession ( authSession );
break ;
}
case CMSG_AUTH_CONTINUED_SESSION :
{
if ( _worldSession )
{
TC_LOG_ERROR ( "network" , "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_CONTINUED_SESSION from %s" , _worldSession -> GetPlayerInfo (). c_str ());
break ;
}
WorldPackets :: Auth :: AuthContinuedSession authSession ( std :: move ( packet ));
authSession . Read ();
HandleAuthContinuedSession ( authSession );
2014-08-22 20:06:24 +02:00
break ;
2014-11-09 00:37:33 +01:00
}
2014-08-22 20:06:24 +02:00
case CMSG_KEEP_ALIVE :
TC_LOG_DEBUG ( "network" , "%s" , opcodeName . c_str ());
sScriptMgr -> OnPacketReceive ( _worldSession , packet );
break ;
case CMSG_LOG_DISCONNECT :
packet . rfinish (); // contains uint32 disconnectReason;
TC_LOG_DEBUG ( "network" , "%s" , opcodeName . c_str ());
sScriptMgr -> OnPacketReceive ( _worldSession , packet );
2014-09-12 20:26:46 +02:00
return true ;
2014-08-22 20:06:24 +02:00
case CMSG_ENABLE_NAGLE :
2014-07-27 12:09:32 +02:00
{
2014-08-22 20:06:24 +02:00
TC_LOG_DEBUG ( "network" , "%s" , opcodeName . c_str ());
sScriptMgr -> OnPacketReceive ( _worldSession , packet );
if ( _worldSession )
_worldSession -> HandleEnableNagleAlgorithm ();
2014-07-27 12:09:32 +02:00
break ;
}
2015-02-23 22:35:26 +01:00
case CMSG_CONNECT_TO_FAILED :
{
WorldPackets :: Auth :: ConnectToFailed connectToFailed ( std :: move ( packet ));
connectToFailed . Read ();
HandleConnectToFailed ( connectToFailed );
break ;
}
2014-08-22 20:06:24 +02:00
default :
2014-07-27 12:09:32 +02:00
{
2014-08-22 20:06:24 +02:00
if ( ! _worldSession )
{
TC_LOG_ERROR ( "network.opcode" , "ProcessIncoming: Client not authed opcode = %u" , uint32 ( opcode ));
CloseSocket ();
2014-09-12 20:26:46 +02:00
return false ;
2014-08-22 20:06:24 +02:00
}
2014-10-12 12:38:04 +02:00
// prevent invalid memory access/crash with custom opcodes
2014-10-27 21:56:12 +00:00
if ( static_cast < uint32 > ( opcode ) >= NUM_OPCODE_HANDLERS )
2014-10-12 15:32:25 +02:00
{
CloseSocket ();
2014-10-12 12:38:04 +02:00
return false ;
2014-10-12 15:32:25 +02:00
}
2014-10-12 12:38:04 +02:00
OpcodeHandler const * handler = opcodeTable [ opcode ];
2014-10-12 15:32:25 +02:00
if ( ! handler )
2014-10-12 12:38:04 +02:00
{
2014-10-25 15:41:42 +01:00
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-10-12 15:32:25 +02:00
return true ;
2014-10-12 12:38:04 +02:00
}
2014-08-22 20:06:24 +02: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 )));
2014-07-27 12:09:32 +02:00
break ;
}
2014-08-12 00:44:26 +02:00
}
2014-08-22 20:06:24 +02:00
}
else
{
2014-09-12 22:09:07 +02:00
std :: string initializer ( reinterpret_cast < char const *> ( _packetBuffer . GetReadPointer ()), std :: min ( _packetBuffer . GetActiveSize (), ClientConnectionInitialize . length ()));
2014-08-22 20:06:24 +02:00
if ( initializer != ClientConnectionInitialize )
2014-08-12 00:44:26 +02:00
{
2014-08-22 20:06:24 +02:00
CloseSocket ();
2014-09-12 20:26:46 +02:00
return false ;
2014-08-12 00:44:26 +02:00
}
2014-08-16 11:23:26 +02:00
2014-11-09 00:37:33 +01:00
_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 )
{
TC_LOG_ERROR ( "network" , "Can't initialize packet compression (zlib: deflateInit) Error code: %i (%s)" , z_res , zError ( z_res ));
CloseSocket ();
return false ;
}
2014-08-22 20:06:24 +02:00
_initialized = true ;
2014-10-23 10:28:46 +02:00
_headerBuffer . Resize ( SizeOfClientHeader [ 1 ][ 0 ]);
2014-09-12 22:09:07 +02:00
_packetBuffer . Reset ();
2014-08-22 20:06:24 +02:00
HandleSendAuthSession ();
2014-07-26 23:26:01 +02:00
}
2014-08-07 19:02:08 +02:00
2014-09-09 19:19:25 +02:00
return true ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
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
TC_LOG_TRACE ( "network.opcode" , "S->C: %s %s" , ( _worldSession ? _worldSession -> GetPlayerInfo () : GetRemoteIpAddress (). to_string ()). c_str (), GetOpcodeNameForLogging ( static_cast < OpcodeServer > ( packet . GetOpcode ())). c_str ());
uint32 packetSize = packet . size ();
uint32 sizeOfHeader = SizeOfServerHeader [ _authCrypt . IsInitialized ()];
if ( packetSize > 0x400 )
packetSize = compressBound ( packetSize ) + sizeof ( CompressedWorldPacket );
std :: unique_lock < std :: mutex > guard ( _writeLock );
#ifndef TC_SOCKET_USE_IOCP
if ( _writeQueue . empty () && _writeBuffer . GetRemainingSpace () >= sizeOfHeader + packetSize )
WritePacketToBuffer ( packet , _writeBuffer );
else
#endif
{
MessageBuffer buffer ( sizeOfHeader + packetSize );
WritePacketToBuffer ( packet , buffer );
QueuePacket ( std :: move ( buffer ), guard );
}
}
2014-10-31 22:36:43 +01:00
void WorldSocket :: WritePacketToBuffer ( WorldPacket const & packet , MessageBuffer & buffer )
2008-10-02 16:23:55 -05:00
{
2014-10-31 22:36:43 +01:00
ServerPktHeader header ;
uint32 sizeOfHeader = SizeOfServerHeader [ _authCrypt . IsInitialized ()];
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 ();
buffer . WriteCompleted ( sizeOfHeader );
2014-07-19 13:30:51 +02:00
2014-11-09 00:37:33 +01:00
if ( packetSize > 0x400 )
2014-10-31 22:36:43 +01:00
{
CompressedWorldPacket cmp ;
cmp . UncompressedSize = packetSize + 4 ;
cmp . UncompressedAdler = adler32 ( adler32 ( 0x9827D8F1 , ( Bytef * ) & opcode , 4 ), 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
2014-10-20 01:27:00 +02:00
if ( _authCrypt . IsInitialized ())
{
2014-10-31 22:36:43 +01:00
header . Normal . Size = packetSize ;
header . Normal . Command = opcode ;
2014-10-20 01:27:00 +02:00
_authCrypt . EncryptSend (( uint8 * ) & header , sizeOfHeader );
}
else
{
2014-10-31 22:36:43 +01:00
header . Setup . Size = packetSize + 4 ;
header . Setup . Command = opcode ;
2014-10-20 01:27:00 +02:00
}
2014-07-24 17:08:53 +02:00
2014-10-31 22:36:43 +01:00
memcpy ( headerPos , & header , sizeOfHeader );
}
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 ();
uint32 bufferSize = deflateBound ( _compressionStream , packet . size () + sizeof ( opcode ));
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 ;
_compressionStream -> avail_in = sizeof ( uint32 );
2014-10-31 22:36:43 +01:00
2014-11-09 00:37:33 +01:00
int32 z_res = deflate ( _compressionStream , Z_BLOCK );
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
}
2014-11-09 00:37:33 +01:00
void WorldSocket :: HandleAuthSession ( WorldPackets :: Auth :: AuthSession & authSession )
2008-10-02 16:23:55 -05:00
{
2013-01-16 11:17:14 +01:00
uint8 security ;
2011-11-20 18:28:18 +01:00
uint32 id ;
LocaleConstant locale ;
2012-02-23 14:07:58 +01:00
SHA1Hash sha ;
2012-07-05 14:16:44 +02:00
BigNumber k ;
2014-07-11 10:22:27 +02:00
bool wardenActive = sWorld -> getBoolConfig ( CONFIG_WARDEN_ENABLED );
2012-07-04 18:24:05 +02:00
2014-07-29 23:45:34 +02:00
// Get the account information from the auth database
2014-10-23 15:00:31 +02:00
// 0 1 2 3 4 5 6 7 8
// SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ?
2014-08-22 20:09:18 +02:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_ACCOUNT_INFO_BY_NAME );
2014-10-31 01:20:53 +01:00
stmt -> setString ( 0 , authSession . Account );
2012-05-30 08:01:02 +02:00
PreparedQueryResult result = LoginDatabase . Query ( stmt );
2009-10-17 15:51:44 -07:00
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-01-16 11:17:14 +01:00
SendAuthResponseError ( AUTH_UNKNOWN_ACCOUNT );
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
2010-05-12 22:03:07 +02:00
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2013-02-07 16:15:23 +01:00
uint8 expansion = fields [ 4 ]. GetUInt8 ();
2010-12-23 23:25:44 +01:00
uint32 world_expansion = sWorld -> getIntConfig ( CONFIG_EXPANSION );
2010-04-07 19:14:10 +02:00
if ( expansion > world_expansion )
2008-10-20 12:23:56 -05:00
expansion = world_expansion ;
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
// As we don't know if attempted login process by ip works, we update last_attempt_ip right away
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_LAST_ATTEMPT_IP );
stmt -> setString ( 0 , address );
2014-10-31 01:20:53 +01:00
stmt -> setString ( 1 , authSession . Account );
2014-05-02 03:44:21 +02:00
LoginDatabase . Execute ( stmt );
// This also allows to check for possible "hack" attempts on account
// id has to be fetched at this point, so that first actual account response that fails can be logged
id = fields [ 0 ]. GetUInt32 ();
2014-08-21 18:18:13 +02:00
k . SetHexStr ( fields [ 1 ]. GetCString ());
// even if auth credentials are bad, try using the session key we have - client cannot read auth response error without it
_authCrypt . Init ( & k );
2014-10-23 10:28:46 +02:00
_headerBuffer . Resize ( SizeOfClientHeader [ 1 ][ 1 ]);
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 ())
{
SendAuthResponseError ( AUTH_REJECT );
TC_LOG_ERROR ( "network" , "WorldSocket::HandleAuthSession: World closed, denying client (%s)." , GetRemoteIpAddress (). to_string (). c_str ());
DelayedCloseSocket ();
return ;
}
2014-10-31 01:20:53 +01:00
if ( authSession . RealmID != realmHandle . Index )
2014-08-21 18:18:13 +02:00
{
SendAuthResponseError ( REALM_LIST_REALM_NOT_FOUND );
TC_LOG_ERROR ( "network" , "WorldSocket::HandleAuthSession: Sent Auth Response (bad realm)." );
DelayedCloseSocket ();
return ;
}
std :: string os = fields [ 8 ]. GetString ();
// Must be done before WorldSession is created
if ( wardenActive && os != "Win" && os != "OSX" )
{
SendAuthResponseError ( AUTH_REJECT );
TC_LOG_ERROR ( "network" , "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s)." , address . c_str (), os . c_str ());
DelayedCloseSocket ();
return ;
}
// Check that Key and account name are the same on client and server
uint32 t = 0 ;
2014-10-31 01:20:53 +01:00
sha . UpdateData ( authSession . Account );
2014-08-21 18:18:13 +02:00
sha . UpdateData (( uint8 * ) & t , 4 );
2014-10-31 01:20:53 +01:00
sha . UpdateData (( uint8 * ) & authSession . LocalChallenge , 4 );
2014-08-21 18:18:13 +02:00
sha . UpdateData (( uint8 * ) & _authSeed , 4 );
sha . UpdateBigNumbers ( & k , NULL );
sha . Finalize ();
2014-10-31 01:20:53 +01:00
if ( memcmp ( sha . GetDigest (), authSession . Digest , SHA_DIGEST_LENGTH ) != 0 )
2014-08-21 18:18:13 +02:00
{
SendAuthResponseError ( AUTH_FAILED );
2014-10-31 01:20:53 +01:00
TC_LOG_ERROR ( "network" , "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s" , id , authSession . Account . c_str (), address . 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).
2010-05-12 22:03:07 +02:00
if ( fields [ 3 ]. GetUInt8 () == 1 ) // if ip is locked
2008-10-04 06:17:19 -05:00
{
2014-09-19 03:30:02 +01:00
if ( strcmp ( fields [ 2 ]. GetCString (), address . c_str ()) != 0 )
2008-10-04 06:17:19 -05:00
{
2013-01-16 11:17:14 +01:00
SendAuthResponseError ( AUTH_FAILED );
2014-05-02 03:44:21 +02:00
TC_LOG_DEBUG ( "network" , "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s)." , fields [ 2 ]. GetCString (), 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 ( 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
2013-02-07 16:15:23 +01:00
int64 mutetime = fields [ 5 ]. GetInt64 ();
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 );
stmt -> setUInt32 ( 1 , id );
LoginDatabase . Execute ( stmt );
2011-07-27 17:51:57 +02:00
}
2009-10-17 15:51:44 -07:00
2014-07-06 01:26:29 +02:00
locale = LocaleConstant ( fields [ 6 ]. GetUInt8 ());
2010-09-28 08:21:51 +03:00
if ( locale >= TOTAL_LOCALES )
2008-11-06 16:10:28 -06:00
locale = LOCALE_enUS ;
2009-10-17 15:51:44 -07:00
2013-02-07 16:15:23 +01:00
uint32 recruiter = fields [ 7 ]. GetUInt32 ();
2014-08-22 20:09:18 +02:00
uint32 battlenetAccountId = 0 ;
2014-10-31 01:20:53 +01:00
if ( authSession . LoginServerType == 1 )
2014-10-23 15:00:31 +02:00
battlenetAccountId = Battlenet :: AccountMgr :: GetIdByGameAccount ( id );
2014-08-22 20:09:18 +02:00
2009-12-21 21:21:39 -06:00
// Checks gmlevel per Realm
2012-05-30 08:01:02 +02:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_GET_GMLEVEL_BY_REALMID );
stmt -> setUInt32 ( 0 , id );
2014-10-17 22:48:06 +02:00
stmt -> setInt32 ( 1 , int32 ( realmHandle . Index ));
2012-05-30 08:01:02 +02:00
result = LoginDatabase . Query ( stmt );
2010-04-07 19:14:10 +02:00
if ( ! result )
2010-01-10 02:12:02 +01:00
security = 0 ;
else
{
2010-05-12 22:03:07 +02:00
fields = result -> Fetch ();
2012-05-30 08:01:02 +02:00
security = fields [ 0 ]. GetUInt8 ();
2010-01-10 02:12:02 +01:00
}
2009-12-21 21:08:29 -06:00
2014-07-29 23:45:34 +02:00
// Re-check account ban (same check as in auth)
2012-05-30 08:01:02 +02:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_BANS );
stmt -> setUInt32 ( 0 , id );
2014-05-02 03:44:21 +02:00
stmt -> setString ( 1 , address );
2012-05-30 08:01:02 +02:00
PreparedQueryResult banresult = LoginDatabase . Query ( stmt );
2009-10-17 15:51:44 -07:00
2008-11-06 16:10:28 -06:00
if ( banresult ) // if account banned
2008-10-04 06:17:19 -05:00
{
2013-01-16 11:17:14 +01:00
SendAuthResponseError ( AUTH_BANNED );
2013-11-08 10:50:51 +01:00
TC_LOG_ERROR ( "network" , "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)." );
2014-05-02 03:44:21 +02:00
sScriptMgr -> OnFailedAccountLogin ( 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 ();
2013-11-08 10:50:51 +01:00
TC_LOG_DEBUG ( "network" , "Allowed Level: %u Player Level %u" , allowedAccountType , AccountTypes ( security ));
2011-11-20 18:28:18 +01:00
if ( allowedAccountType > SEC_PLAYER && AccountTypes ( security ) < allowedAccountType )
2008-10-06 04:14:44 -05:00
{
2013-01-16 11:17:14 +01:00
SendAuthResponseError ( AUTH_UNAVAILABLE );
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "network" , "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough" );
2014-05-02 03:44:21 +02:00
sScriptMgr -> OnFailedAccountLogin ( 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
}
2013-11-08 10:50:51 +01:00
TC_LOG_DEBUG ( "network" , "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s." ,
2014-10-31 01:20:53 +01:00
authSession . Account . c_str (), address . c_str ());
2009-10-17 15:51:44 -07:00
2011-08-03 09:28:12 -07:00
// Check if this user is by any chance a recruiter
2012-05-30 08:01:02 +02:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_ACCOUNT_RECRUITER );
stmt -> setUInt32 ( 0 , id );
result = LoginDatabase . Query ( stmt );
2011-08-03 09:28:12 -07:00
bool isRecruiter = false ;
if ( result )
isRecruiter = true ;
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 );
2014-10-31 01:20:53 +01:00
stmt -> setString ( 1 , authSession . Account );
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
sScriptMgr -> OnAccountLogin ( id );
2009-10-17 15:51:44 -07:00
2014-08-21 23:59:00 +02:00
_worldSession = new WorldSession ( id , battlenetAccountId , shared_from_this (), AccountTypes ( security ), expansion , mutetime , locale , recruiter , isRecruiter );
2014-07-06 01:26:29 +02:00
_worldSession -> LoadGlobalAccountData ();
_worldSession -> LoadTutorialsData ();
2014-10-31 01:20:53 +01:00
_worldSession -> ReadAddonsInfo ( authSession . AddonInfo );
2014-07-06 01:26:29 +02:00
_worldSession -> LoadPermissions ();
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 )
2014-07-06 01:26:29 +02:00
_worldSession -> InitWarden ( & k , os );
2012-02-19 13:51:16 +01:00
2014-07-06 01:26:29 +02:00
sWorld -> AddSession ( _worldSession );
}
2009-10-17 15:51:44 -07:00
2014-11-09 00:37:33 +01:00
void WorldSocket :: HandleAuthContinuedSession ( WorldPackets :: Auth :: AuthContinuedSession & authSession )
{
uint32 accountId = PAIR64_LOPART ( authSession . Key );
_type = ConnectionType ( PAIR64_HIPART ( authSession . Key ));
2014-11-09 21:36:06 +01:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_ACCOUNT_INFO_CONTINUED_SESSION );
stmt -> setUInt32 ( 0 , accountId );
PreparedQueryResult result = LoginDatabase . Query ( stmt );
2014-11-09 00:37:33 +01:00
if ( ! result )
{
SendAuthResponseError ( AUTH_UNKNOWN_ACCOUNT );
DelayedCloseSocket ();
return ;
}
Field * fields = result -> Fetch ();
std :: string login = fields [ 0 ]. GetString ();
BigNumber k ;
k . SetHexStr ( fields [ 1 ]. GetCString ());
_authCrypt . Init ( & k , _encryptSeed . AsByteArray (). get (), _decryptSeed . AsByteArray (). get ());
_headerBuffer . Resize ( SizeOfClientHeader [ 1 ][ 1 ]);
SHA1Hash sha ;
sha . UpdateData ( login );
sha . UpdateBigNumbers ( & k , NULL );
sha . UpdateData (( uint8 * ) & _authSeed , 4 );
sha . Finalize ();
if ( memcmp ( sha . GetDigest (), authSession . Digest , sha . GetLength ()))
{
SendAuthResponseError ( AUTH_UNKNOWN_ACCOUNT );
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 ;
}
_worldSession = sWorld -> FindSession ( accountId );
if ( ! _worldSession )
{
SendAuthResponseError ( AUTH_SESSION_EXPIRED );
TC_LOG_ERROR ( "network" , "WorldSocket::HandleAuthContinuedSession: No active session found for account: %u ('%s') address: %s" , accountId , login . c_str (), GetRemoteIpAddress (). to_string (). c_str ());
DelayedCloseSocket ();
return ;
}
WorldPackets :: Auth :: ResumeComms resumeComms ;
SendPacket ( * resumeComms . Write ());
_worldSession -> AddInstanceConnection ( shared_from_this ());
_worldSession -> HandleContinuePlayerLogin ();
}
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 ());
_worldSession -> AbortLogin ( WorldPackets :: Character :: LoginFailureReason :: NoWorld );
break ;
}
default :
return ;
}
}
//else
//{
// transfer_aborted when/if we get map node redirection
// SendPacket(*WorldPackets::Auth::ResumeComms().Write());
//}
}
}
2014-07-07 22:03:41 +02:00
void WorldSocket :: SendAuthResponseError ( uint8 code )
2014-07-06 01:26:29 +02:00
{
2014-10-30 02:04:54 +00:00
WorldPackets :: Auth :: AuthResponse response ;
response . SuccessInfo . HasValue = false ;
response . WaitInfo . HasValue = false ;
response . Result = code ;
2014-11-03 20:23:21 +01:00
SendPacket ( * response . Write ());
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2014-07-07 22:03:41 +02:00
void WorldSocket :: HandlePing ( WorldPacket & recvPacket )
2008-10-02 16:23:55 -05:00
{
2008-11-06 16:10:28 -06:00
uint32 ping ;
uint32 latency ;
2009-10-17 15:51:44 -07:00
2008-11-06 16:10:28 -06:00
// Get the ping packet content
2012-07-26 18:36:26 +02:00
recvPacket >> ping ;
2014-10-19 17:23:48 +02:00
recvPacket >> latency ;
2009-10-17 15:51:44 -07:00
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
{
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
2014-07-25 18:01:27 +01:00
CloseSocket ();
2014-07-06 18:35:04 +02:00
return ;
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
if ( _worldSession )
{
_worldSession -> SetLatency ( latency );
_worldSession -> ResetClientTimeDelay ();
}
else
2008-11-06 16:10:28 -06:00
{
2014-07-26 23:26:01 +02:00
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 ());
2009-10-17 15:51:44 -07:00
2014-07-25 18:01:27 +01:00
CloseSocket ();
2014-07-06 18:35:04 +02:00
return ;
2008-11-06 16:10:28 -06:00
}
2009-10-17 15:51:44 -07:00
2012-07-12 14:16:20 +02:00
WorldPacket packet ( SMSG_PONG , 4 );
2008-11-06 16:10:28 -06:00
packet << ping ;
2014-09-09 19:19:25 +02:00
return SendPacket ( packet );
2014-08-11 17:28:10 +02:00
}