2010-10-07 15:35:36 +02:00
/*
2011-01-01 15:01:13 +01:00
* Copyright (C) 2008-2011 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-06-06 23:08:23 +02:00
*/
2010-12-27 09:02:02 -08:00
#include <openssl/md5.h>
2010-06-06 23:08:23 +02:00
#include "Common.h"
#include "Database/DatabaseEnv.h"
#include "ByteBuffer.h"
2010-07-29 01:22:45 +02:00
#include "Configuration/Config.h"
2010-06-06 23:08:23 +02:00
#include "Log.h"
#include "RealmList.h"
#include "AuthSocket.h"
#include "AuthCodes.h"
2010-06-08 01:20:06 +02:00
#include "SHA1.h"
2011-10-18 17:55:05 +02:00
#include "openssl/crypto.h"
2010-06-06 23:08:23 +02:00
#define ChunkSize 2048
enum eAuthCmd
{
2010-12-27 09:02:02 -08:00
AUTH_LOGON_CHALLENGE = 0x00 ,
AUTH_LOGON_PROOF = 0x01 ,
AUTH_RECONNECT_CHALLENGE = 0x02 ,
AUTH_RECONNECT_PROOF = 0x03 ,
REALM_LIST = 0x10 ,
XFER_INITIATE = 0x30 ,
XFER_DATA = 0x31 ,
XFER_ACCEPT = 0x32 ,
XFER_RESUME = 0x33 ,
XFER_CANCEL = 0x34
2010-06-06 23:08:23 +02:00
};
enum eStatus
{
2010-12-27 09:02:02 -08:00
STATUS_CONNECTED = 0 ,
2010-06-06 23:08:23 +02:00
STATUS_AUTHED
};
2011-04-29 20:47:02 +02:00
// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push, N), also any gcc version not support it at some paltform
2010-06-06 23:08:23 +02:00
#if defined(__GNUC__)
#pragma pack(1)
#else
2011-04-29 20:47:02 +02:00
#pragma pack(push, 1)
2010-06-06 23:08:23 +02:00
#endif
typedef struct AUTH_LOGON_CHALLENGE_C
{
uint8 cmd ;
uint8 error ;
uint16 size ;
uint8 gamename [ 4 ];
uint8 version1 ;
uint8 version2 ;
uint8 version3 ;
uint16 build ;
uint8 platform [ 4 ];
uint8 os [ 4 ];
uint8 country [ 4 ];
uint32 timezone_bias ;
uint32 ip ;
uint8 I_len ;
uint8 I [ 1 ];
} sAuthLogonChallenge_C ;
typedef struct AUTH_LOGON_PROOF_C
{
uint8 cmd ;
uint8 A [ 32 ];
uint8 M1 [ 20 ];
uint8 crc_hash [ 20 ];
uint8 number_of_keys ;
uint8 securityFlags ; // 0x00-0x04
} sAuthLogonProof_C ;
typedef struct AUTH_LOGON_PROOF_S
{
uint8 cmd ;
uint8 error ;
uint8 M2 [ 20 ];
uint32 unk1 ;
uint32 unk2 ;
uint16 unk3 ;
} sAuthLogonProof_S ;
typedef struct AUTH_LOGON_PROOF_S_OLD
{
uint8 cmd ;
uint8 error ;
uint8 M2 [ 20 ];
uint32 unk2 ;
} sAuthLogonProof_S_Old ;
typedef struct AUTH_RECONNECT_PROOF_C
{
uint8 cmd ;
uint8 R1 [ 16 ];
uint8 R2 [ 20 ];
uint8 R3 [ 20 ];
uint8 number_of_keys ;
} sAuthReconnectProof_C ;
typedef struct XFER_INIT
{
uint8 cmd ; // XFER_INITIATE
uint8 fileNameLen ; // strlen(fileName);
uint8 fileName [ 5 ]; // fileName[fileNameLen]
uint64 file_size ; // file size (bytes)
uint8 md5 [ MD5_DIGEST_LENGTH ]; // MD5
} XFER_INIT ;
typedef struct XFER_DATA
{
uint8 opcode ;
uint16 data_size ;
uint8 data [ ChunkSize ];
} XFER_DATA_STRUCT ;
typedef struct AuthHandler
{
eAuthCmd cmd ;
uint32 status ;
bool ( AuthSocket ::* handler )( void );
} AuthHandler ;
// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some paltform
#if defined(__GNUC__)
#pragma pack()
#else
#pragma pack(pop)
#endif
2010-11-16 14:29:01 +01:00
// Launch a thread to transfer a patch to the client
2010-06-06 23:08:23 +02:00
class PatcherRunnable : public ACE_Based :: Runnable
{
2010-12-27 09:02:02 -08:00
public :
2011-09-29 09:32:55 +02:00
PatcherRunnable ( class AuthSocket * );
2010-12-27 09:02:02 -08:00
void run ();
2010-06-06 23:08:23 +02:00
2010-12-27 09:02:02 -08:00
private :
2011-09-15 14:08:17 +02:00
AuthSocket * mySocket ;
2010-06-06 23:08:23 +02:00
};
typedef struct PATCH_INFO
{
uint8 md5 [ MD5_DIGEST_LENGTH ];
} PATCH_INFO ;
2010-11-16 14:29:01 +01:00
// Caches MD5 hash of client patches present on the server
2010-06-06 23:08:23 +02:00
class Patcher
{
2010-12-27 09:02:02 -08:00
public :
typedef std :: map < std :: string , PATCH_INFO *> Patches ;
~ Patcher ();
Patcher ();
Patches :: const_iterator begin () const { return _patches . begin (); }
Patches :: const_iterator end () const { return _patches . end (); }
void LoadPatchMD5 ( char * );
2011-04-29 20:47:02 +02:00
bool GetHash ( char * pat , uint8 mymd5 [ 16 ]);
2010-12-27 09:02:02 -08:00
private :
void LoadPatchesInfo ();
Patches _patches ;
2010-06-06 23:08:23 +02:00
};
const AuthHandler table [] =
{
{ AUTH_LOGON_CHALLENGE , STATUS_CONNECTED , & AuthSocket :: _HandleLogonChallenge },
{ AUTH_LOGON_PROOF , STATUS_CONNECTED , & AuthSocket :: _HandleLogonProof },
{ AUTH_RECONNECT_CHALLENGE , STATUS_CONNECTED , & AuthSocket :: _HandleReconnectChallenge },
{ AUTH_RECONNECT_PROOF , STATUS_CONNECTED , & AuthSocket :: _HandleReconnectProof },
{ REALM_LIST , STATUS_AUTHED , & AuthSocket :: _HandleRealmList },
{ XFER_ACCEPT , STATUS_CONNECTED , & AuthSocket :: _HandleXferAccept },
{ XFER_RESUME , STATUS_CONNECTED , & AuthSocket :: _HandleXferResume },
{ XFER_CANCEL , STATUS_CONNECTED , & AuthSocket :: _HandleXferCancel }
};
2010-12-27 09:02:02 -08:00
#define AUTH_TOTAL_COMMANDS 8
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Holds the MD5 hash of client patches present on the server
2010-06-06 23:08:23 +02:00
Patcher PatchesCache ;
2010-11-16 14:29:01 +01:00
// Constructor - set the N and g values for SRP6
2010-06-06 23:08:23 +02:00
AuthSocket :: AuthSocket ( RealmSocket & socket ) : socket_ ( socket )
{
N . SetHexStr ( "894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7" );
g . SetDword ( 7 );
_authed = false ;
_accountSecurityLevel = SEC_PLAYER ;
}
2010-11-16 14:29:01 +01:00
// Close patch file descriptor before leaving
AuthSocket ::~ AuthSocket ( void ) {}
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Accept the connection and set the s random value for SRP6
2010-06-06 23:08:23 +02:00
void AuthSocket :: OnAccept ( void )
{
2010-12-23 23:25:44 +01:00
sLog -> outBasic ( "Accepting connection from '%s'" , socket (). get_remote_address (). c_str ());
2010-06-06 23:08:23 +02:00
}
void AuthSocket :: OnClose ( void )
{
2011-02-20 20:16:34 +01:00
sLog -> outDebug ( LOG_FILTER_NETWORKIO , "AuthSocket::OnClose" );
2010-06-06 23:08:23 +02:00
}
2010-11-16 14:29:01 +01:00
// Read the packet from the client
2010-06-06 23:08:23 +02:00
void AuthSocket :: OnRead ()
{
uint8 _cmd ;
while ( 1 )
{
if ( ! socket (). recv_soft (( char * ) & _cmd , 1 ))
return ;
size_t i ;
2010-11-16 14:29:01 +01:00
// Circle through known commands and call the correct command handler
2010-06-06 23:08:23 +02:00
for ( i = 0 ; i < AUTH_TOTAL_COMMANDS ; ++ i )
{
2010-12-27 09:02:02 -08:00
if (( uint8 ) table [ i ]. cmd == _cmd && ( table [ i ]. status == STATUS_CONNECTED || ( _authed && table [ i ]. status == STATUS_AUTHED )))
2010-06-06 23:08:23 +02:00
{
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "[Auth] got data for cmd %u recv length %u" , ( uint32 ) _cmd , ( uint32 ) socket (). recv_len ());
2010-06-06 23:08:23 +02:00
if ( ! ( * this . * table [ i ]. handler )())
{
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "Command handler failed for cmd %u recv length %u" , ( uint32 ) _cmd , ( uint32 ) socket (). recv_len ());
2010-06-06 23:08:23 +02:00
return ;
}
break ;
}
}
// Report unknown packets in the error log
if ( i == AUTH_TOTAL_COMMANDS )
{
2010-12-23 23:25:44 +01:00
sLog -> outError ( "[Auth] got unknown packet from '%s'" , socket (). get_remote_address (). c_str ());
2010-06-06 23:08:23 +02:00
socket (). shutdown ();
return ;
}
}
}
2010-11-16 14:29:01 +01:00
// Make the SRP6 calculation from hash in dB
2010-06-06 23:08:23 +02:00
void AuthSocket :: _SetVSFields ( const std :: string & rI )
{
s . SetRand ( s_BYTE_SIZE * 8 );
BigNumber I ;
I . SetHexStr ( rI . c_str ());
// In case of leading zeros in the rI hash, restore them
uint8 mDigest [ SHA_DIGEST_LENGTH ];
memset ( mDigest , 0 , SHA_DIGEST_LENGTH );
if ( I . GetNumBytes () <= SHA_DIGEST_LENGTH )
memcpy ( mDigest , I . AsByteArray (), I . GetNumBytes ());
std :: reverse ( mDigest , mDigest + SHA_DIGEST_LENGTH );
2010-08-08 04:49:04 +02:00
SHA1Hash sha ;
2010-06-06 23:08:23 +02:00
sha . UpdateData ( s . AsByteArray (), s . GetNumBytes ());
sha . UpdateData ( mDigest , SHA_DIGEST_LENGTH );
sha . Finalize ();
BigNumber x ;
x . SetBinary ( sha . GetDigest (), sha . GetLength ());
v = g . ModExp ( x , N );
2010-12-27 09:02:02 -08:00
2010-06-06 23:08:23 +02:00
// No SQL injection (username escaped)
const char * v_hex , * s_hex ;
v_hex = v . AsHexStr ();
s_hex = s . AsHexStr ();
2010-09-03 01:00:49 +02:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SET_VS );
stmt -> setString ( 0 , v_hex );
stmt -> setString ( 1 , s_hex );
2010-09-12 17:04:19 +02:00
stmt -> setString ( 2 , _login );
2010-09-03 01:00:49 +02:00
LoginDatabase . Execute ( stmt );
2010-06-06 23:08:23 +02:00
OPENSSL_free (( void * ) v_hex );
OPENSSL_free (( void * ) s_hex );
}
2010-11-16 14:29:01 +01:00
// Logon Challenge command handler
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleLogonChallenge ()
{
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "Entering _HandleLogonChallenge" );
2010-06-06 23:08:23 +02:00
if ( socket (). recv_len () < sizeof ( sAuthLogonChallenge_C ))
return false ;
2010-11-16 14:29:01 +01:00
// Read the first 4 bytes (header) to get the length of the remaining of the packet
2010-06-06 23:08:23 +02:00
std :: vector < uint8 > buf ;
buf . resize ( 4 );
socket (). recv (( char * ) & buf [ 0 ], 4 );
2010-12-04 11:19:41 -08:00
#if TRINITY_ENDIAN == TRINITY_BIGENDIAN
2010-06-06 23:08:23 +02:00
EndianConvert ( * (( uint16 * )( buf [ 0 ])));
2010-12-04 11:19:41 -08:00
#endif
2010-06-06 23:08:23 +02:00
uint16 remaining = (( sAuthLogonChallenge_C * ) & buf [ 0 ]) -> size ;
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "[AuthChallenge] got header, body is %#04x bytes" , remaining );
2010-06-06 23:08:23 +02:00
if (( remaining < sizeof ( sAuthLogonChallenge_C ) - buf . size ()) || ( socket (). recv_len () < remaining ))
return false ;
//No big fear of memory outage (size is int16, i.e. < 65536)
buf . resize ( remaining + buf . size () + 1 );
buf [ buf . size () - 1 ] = 0 ;
sAuthLogonChallenge_C * ch = ( sAuthLogonChallenge_C * ) & buf [ 0 ];
2010-11-16 14:29:01 +01:00
// Read the remaining of the packet
2010-06-06 23:08:23 +02:00
socket (). recv (( char * ) & buf [ 4 ], remaining );
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "[AuthChallenge] got full packet, %#04x bytes" , ch -> size );
sLog -> outStaticDebug ( "[AuthChallenge] name(%d): '%s'" , ch -> I_len , ch -> I );
2010-06-06 23:08:23 +02:00
// BigEndian code, nop in little endian case
// size already converted
2010-12-04 11:19:41 -08:00
#if TRINITY_ENDIAN == TRINITY_BIGENDIAN
2010-06-06 23:08:23 +02:00
EndianConvert ( * (( uint32 * )( & ch -> gamename [ 0 ])));
EndianConvert ( ch -> build );
EndianConvert ( * (( uint32 * )( & ch -> platform [ 0 ])));
EndianConvert ( * (( uint32 * )( & ch -> os [ 0 ])));
EndianConvert ( * (( uint32 * )( & ch -> country [ 0 ])));
EndianConvert ( ch -> timezone_bias );
EndianConvert ( ch -> ip );
2010-12-04 11:19:41 -08:00
#endif
2010-06-06 23:08:23 +02:00
ByteBuffer pkt ;
_login = ( const char * ) ch -> I ;
_build = ch -> build ;
2010-12-04 11:19:41 -08:00
_expversion = ( AuthHelper :: IsPostBCAcceptedClientBuild ( _build ) ? POST_BC_EXP_FLAG : NO_VALID_EXP_FLAG ) | ( AuthHelper :: IsPreBCAcceptedClientBuild ( _build ) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG );
2010-06-06 23:08:23 +02:00
2010-12-04 11:19:41 -08:00
pkt << ( uint8 ) AUTH_LOGON_CHALLENGE ;
pkt << ( uint8 ) 0x00 ;
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Verify that this IP is not in the ip_banned table
2010-12-04 11:19:41 -08:00
LoginDatabase . Execute ( LoginDatabase . GetPreparedStatement ( LOGIN_SET_EXPIREDIPBANS ));
2010-09-12 11:06:26 +02:00
const std :: string & ip_address = socket (). get_remote_address ();
2010-12-04 11:19:41 -08:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_GET_IPBANNED );
2010-09-12 11:06:26 +02:00
stmt -> setString ( 0 , ip_address );
PreparedQueryResult result = LoginDatabase . Query ( stmt );
2010-06-06 23:08:23 +02:00
if ( result )
{
pkt << ( uint8 ) WOW_FAIL_BANNED ;
2010-12-23 23:25:44 +01:00
sLog -> outBasic ( "[AuthChallenge] Banned ip %s tried to login!" , ip_address . c_str ());
2010-06-06 23:08:23 +02:00
}
else
{
2010-11-16 14:29:01 +01:00
// Get the account details from the account table
2010-09-12 11:06:26 +02:00
// No SQL injection (prepared statement)
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_GET_LOGONCHALLENGE );
stmt -> setString ( 0 , _login );
2010-06-06 23:08:23 +02:00
2010-09-11 21:22:15 +02:00
PreparedQueryResult res2 = LoginDatabase . Query ( stmt );
if ( res2 )
2010-06-06 23:08:23 +02:00
{
2010-09-24 22:16:21 +02:00
Field * fields = res2 -> Fetch ();
2010-11-16 14:29:01 +01:00
// If the IP is 'locked', check that the player comes indeed from the correct IP address
2010-06-06 23:08:23 +02:00
bool locked = false ;
2010-11-16 14:29:01 +01:00
if ( fields [ 2 ]. GetUInt8 () == 1 ) // if ip is locked
2010-06-06 23:08:23 +02:00
{
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "[AuthChallenge] Account '%s' is locked to IP - '%s'" , _login . c_str (), fields [ 3 ]. GetCString ());
sLog -> outStaticDebug ( "[AuthChallenge] Player address is '%s'" , ip_address . c_str ());
2010-11-16 14:29:01 +01:00
2010-09-24 22:16:21 +02:00
if ( strcmp ( fields [ 3 ]. GetCString (), ip_address . c_str ()))
2010-06-06 23:08:23 +02:00
{
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "[AuthChallenge] Account IP differs" );
2010-06-06 23:08:23 +02:00
pkt << ( uint8 ) WOW_FAIL_SUSPENDED ;
2010-12-04 11:19:41 -08:00
locked = true ;
2010-06-06 23:08:23 +02:00
}
else
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "[AuthChallenge] Account IP matches" );
2010-06-06 23:08:23 +02:00
}
else
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "[AuthChallenge] Account '%s' is not locked to ip" , _login . c_str ());
2010-06-06 23:08:23 +02:00
if ( ! locked )
{
//set expired bans to inactive
2010-12-04 11:19:41 -08:00
LoginDatabase . Execute ( LoginDatabase . GetPreparedStatement ( LOGIN_SET_EXPIREDACCBANS ));
2010-09-12 11:06:26 +02:00
2010-11-16 14:29:01 +01:00
// If the account is banned, reject the logon attempt
2010-09-12 11:06:26 +02:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_GET_ACCBANNED );
2010-09-24 22:16:21 +02:00
stmt -> setUInt32 ( 0 , fields [ 1 ]. GetUInt32 ());
2010-09-12 11:06:26 +02:00
PreparedQueryResult banresult = LoginDatabase . Query ( stmt );
2010-06-06 23:08:23 +02:00
if ( banresult )
{
2010-09-24 22:16:21 +02:00
if (( * banresult )[ 0 ]. GetUInt64 () == ( * banresult )[ 1 ]. GetUInt64 ())
2010-06-06 23:08:23 +02:00
{
2010-12-04 11:19:41 -08:00
pkt << ( uint8 ) WOW_FAIL_BANNED ;
2010-12-23 23:25:44 +01:00
sLog -> outBasic ( "[AuthChallenge] Banned account %s tried to login!" , _login . c_str ());
2010-06-06 23:08:23 +02:00
}
else
{
2010-12-04 11:19:41 -08:00
pkt << ( uint8 ) WOW_FAIL_SUSPENDED ;
2010-12-23 23:25:44 +01:00
sLog -> outBasic ( "[AuthChallenge] Temporarily banned account %s tried to login!" , _login . c_str ());
2010-06-06 23:08:23 +02:00
}
}
else
{
2010-11-16 14:29:01 +01:00
// Get the password from the account table, upper it, and make the SRP6 calculation
2010-09-24 22:16:21 +02:00
std :: string rI = fields [ 0 ]. GetString ();
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Don't calculate (v, s) if there are already some in the database
2010-09-24 22:16:21 +02:00
std :: string databaseV = fields [ 5 ]. GetString ();
std :: string databaseS = fields [ 6 ]. GetString ();
2010-06-06 23:08:23 +02:00
2011-02-20 20:16:34 +01:00
sLog -> outDebug ( LOG_FILTER_NETWORKIO , "database authentication values: v='%s' s='%s'" , databaseV . c_str (), databaseS . c_str ());
2010-06-06 23:08:23 +02:00
2010-12-04 11:19:41 -08:00
// multiply with 2 since bytes are stored as hexstring
if ( databaseV . size () != s_BYTE_SIZE * 2 || databaseS . size () != s_BYTE_SIZE * 2 )
2010-06-06 23:08:23 +02:00
_SetVSFields ( rI );
else
{
s . SetHexStr ( databaseS . c_str ());
v . SetHexStr ( databaseV . c_str ());
}
b . SetRand ( 19 * 8 );
BigNumber gmod = g . ModExp ( b , N );
B = (( v * 3 ) + gmod ) % N ;
ASSERT ( gmod . GetNumBytes () <= 32 );
BigNumber unk3 ;
unk3 . SetRand ( 16 * 8 );
2010-11-16 14:29:01 +01:00
// Fill the response packet with the result
2010-06-06 23:08:23 +02:00
pkt << uint8 ( WOW_SUCCESS );
// B may be calculated < 32B so we force minimal length to 32B
pkt . append ( B . AsByteArray ( 32 ), 32 ); // 32 bytes
pkt << uint8 ( 1 );
pkt . append ( g . AsByteArray (), 1 );
pkt << uint8 ( 32 );
pkt . append ( N . AsByteArray ( 32 ), 32 );
pkt . append ( s . AsByteArray (), s . GetNumBytes ()); // 32 bytes
pkt . append ( unk3 . AsByteArray ( 16 ), 16 );
uint8 securityFlags = 0 ;
pkt << uint8 ( securityFlags ); // security flags (0x0...0x04)
2010-11-16 14:29:01 +01:00
if ( securityFlags & 0x01 ) // PIN input
2010-06-06 23:08:23 +02:00
{
pkt << uint32 ( 0 );
pkt << uint64 ( 0 ) << uint64 ( 0 ); // 16 bytes hash?
}
2010-11-16 14:29:01 +01:00
if ( securityFlags & 0x02 ) // Matrix input
2010-06-06 23:08:23 +02:00
{
pkt << uint8 ( 0 );
pkt << uint8 ( 0 );
pkt << uint8 ( 0 );
pkt << uint8 ( 0 );
pkt << uint64 ( 0 );
}
2010-11-16 14:29:01 +01:00
if ( securityFlags & 0x04 ) // Security token input
2010-06-06 23:08:23 +02:00
pkt << uint8 ( 1 );
2010-09-24 22:16:21 +02:00
uint8 secLevel = fields [ 4 ]. GetUInt8 ();
2010-06-06 23:08:23 +02:00
_accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes ( secLevel ) : SEC_ADMINISTRATOR ;
_localizationName . resize ( 4 );
for ( int i = 0 ; i < 4 ; ++ i )
_localizationName [ i ] = ch -> country [ 4 - i - 1 ];
2010-12-27 09:02:02 -08:00
sLog -> outBasic ( "[AuthChallenge] account %s is using '%c%c%c%c' locale (%u)" , _login . c_str (), ch -> country [ 3 ], ch -> country [ 2 ], ch -> country [ 1 ], ch -> country [ 0 ], GetLocaleByName ( _localizationName ));
2010-06-06 23:08:23 +02:00
}
}
}
2010-11-16 14:29:01 +01:00
else //no account
2010-12-04 11:19:41 -08:00
pkt << ( uint8 ) WOW_FAIL_UNKNOWN_ACCOUNT ;
2010-06-06 23:08:23 +02:00
}
socket (). send (( char const * ) pkt . contents (), pkt . size ());
return true ;
}
2010-11-16 14:29:01 +01:00
// Logon Proof command handler
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleLogonProof ()
{
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "Entering _HandleLogonProof" );
2010-11-16 14:29:01 +01:00
// Read the packet
2010-06-06 23:08:23 +02:00
sAuthLogonProof_C lp ;
if ( ! socket (). recv (( char * ) & lp , sizeof ( sAuthLogonProof_C )))
return false ;
2010-11-16 14:29:01 +01:00
// If the client has no valid version
2010-06-06 23:08:23 +02:00
if ( _expversion == NO_VALID_EXP_FLAG )
{
2010-11-16 14:29:01 +01:00
// Check if we have the appropriate patch on the disk
2011-02-20 20:16:34 +01:00
sLog -> outDebug ( LOG_FILTER_NETWORKIO , "Client with invalid version, patching is not implemented" );
2010-06-06 23:08:23 +02:00
socket (). shutdown ();
return true ;
}
2010-11-16 14:29:01 +01:00
// Continue the SRP6 calculation based on data received from the client
2010-06-06 23:08:23 +02:00
BigNumber A ;
A . SetBinary ( lp . A , 32 );
2010-12-04 11:19:41 -08:00
// SRP safeguard: abort if A == 0
2010-06-06 23:08:23 +02:00
if ( A . isZero ())
{
socket (). shutdown ();
return true ;
}
2010-08-08 04:49:04 +02:00
SHA1Hash sha ;
2010-06-06 23:08:23 +02:00
sha . UpdateBigNumbers ( & A , & B , NULL );
sha . Finalize ();
BigNumber u ;
u . SetBinary ( sha . GetDigest (), 20 );
BigNumber S = ( A * ( v . ModExp ( u , N ))). ModExp ( b , N );
uint8 t [ 32 ];
uint8 t1 [ 16 ];
uint8 vK [ 40 ];
memcpy ( t , S . AsByteArray ( 32 ), 32 );
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
for ( int i = 0 ; i < 16 ; ++ i )
t1 [ i ] = t [ i * 2 ];
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
sha . Initialize ();
sha . UpdateData ( t1 , 16 );
sha . Finalize ();
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
for ( int i = 0 ; i < 20 ; ++ i )
vK [ i * 2 ] = sha . GetDigest ()[ i ];
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
for ( int i = 0 ; i < 16 ; ++ i )
t1 [ i ] = t [ i * 2 + 1 ];
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
sha . Initialize ();
sha . UpdateData ( t1 , 16 );
sha . Finalize ();
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
for ( int i = 0 ; i < 20 ; ++ i )
vK [ i * 2 + 1 ] = sha . GetDigest ()[ i ];
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
K . SetBinary ( vK , 40 );
uint8 hash [ 20 ];
sha . Initialize ();
sha . UpdateBigNumbers ( & N , NULL );
sha . Finalize ();
memcpy ( hash , sha . GetDigest (), 20 );
sha . Initialize ();
sha . UpdateBigNumbers ( & g , NULL );
sha . Finalize ();
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
for ( int i = 0 ; i < 20 ; ++ i )
hash [ i ] ^= sha . GetDigest ()[ i ];
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
BigNumber t3 ;
t3 . SetBinary ( hash , 20 );
sha . Initialize ();
sha . UpdateData ( _login );
sha . Finalize ();
uint8 t4 [ SHA_DIGEST_LENGTH ];
memcpy ( t4 , sha . GetDigest (), SHA_DIGEST_LENGTH );
sha . Initialize ();
sha . UpdateBigNumbers ( & t3 , NULL );
sha . UpdateData ( t4 , SHA_DIGEST_LENGTH );
sha . UpdateBigNumbers ( & s , & A , & B , & K , NULL );
sha . Finalize ();
BigNumber M ;
M . SetBinary ( sha . GetDigest (), 20 );
2010-11-16 14:29:01 +01:00
// Check if SRP6 results match (password is correct), else send an error
2010-06-06 23:08:23 +02:00
if ( ! memcmp ( M . AsByteArray (), lp . M1 , 20 ))
{
2010-12-23 23:25:44 +01:00
sLog -> outBasic ( "User '%s' successfully authenticated" , _login . c_str ());
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account
2010-06-06 23:08:23 +02:00
// No SQL injection (escaped user name) and IP address as received by socket
2010-12-27 09:02:02 -08:00
const char * K_hex = K . AsHexStr ();
2010-09-12 01:40:27 +02:00
2010-12-27 09:02:02 -08:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SET_LOGONPROOF );
2010-09-03 01:00:49 +02:00
stmt -> setString ( 0 , K_hex );
stmt -> setString ( 1 , socket (). get_remote_address (). c_str ());
stmt -> setUInt32 ( 2 , GetLocaleByName ( _localizationName ));
2010-09-12 17:04:19 +02:00
stmt -> setString ( 3 , _login );
2010-09-03 01:00:49 +02:00
LoginDatabase . Execute ( stmt );
2010-06-06 23:08:23 +02:00
OPENSSL_free (( void * ) K_hex );
2010-11-16 14:29:01 +01:00
// Finish SRP6 and send the final result to the client
2010-06-06 23:08:23 +02:00
sha . Initialize ();
sha . UpdateBigNumbers ( & A , & M , & K , NULL );
sha . Finalize ();
2010-12-04 11:19:41 -08:00
if ( _expversion & POST_BC_EXP_FLAG ) // 2.x and 3.x clients
2010-06-06 23:08:23 +02:00
{
sAuthLogonProof_S proof ;
memcpy ( proof . M2 , sha . GetDigest (), 20 );
proof . cmd = AUTH_LOGON_PROOF ;
proof . error = 0 ;
2011-11-26 01:53:44 +01:00
proof . unk1 = 0x00800000 ; // Accountflags. 0x01 = GM, 0x08 = Trial, 0x00800000 = Pro pass (arena tournament)
proof . unk2 = 0x00 ; // SurveyId
2010-06-06 23:08:23 +02:00
proof . unk3 = 0x00 ;
socket (). send (( char * ) & proof , sizeof ( proof ));
}
else
{
sAuthLogonProof_S_Old proof ;
memcpy ( proof . M2 , sha . GetDigest (), 20 );
proof . cmd = AUTH_LOGON_PROOF ;
proof . error = 0 ;
proof . unk2 = 0x00 ;
socket (). send (( char * ) & proof , sizeof ( proof ));
}
_authed = true ;
}
else
{
2010-12-27 09:19:44 -08:00
char data [ 4 ] = { AUTH_LOGON_PROOF , WOW_FAIL_UNKNOWN_ACCOUNT , 3 , 0 };
2010-06-06 23:08:23 +02:00
socket (). send ( data , sizeof ( data ));
2010-09-25 22:03:57 +02:00
2010-12-27 09:02:02 -08:00
sLog -> outBasic ( "[AuthChallenge] account %s tried to login with wrong password!" , _login . c_str ());
2010-06-06 23:08:23 +02:00
2011-09-28 13:00:43 +02:00
uint32 MaxWrongPassCount = ConfigMgr :: GetIntDefault ( "WrongPass.MaxCount" , 0 );
2010-06-06 23:08:23 +02:00
if ( MaxWrongPassCount > 0 )
{
//Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP
2010-12-04 11:19:41 -08:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SET_FAILEDLOGINS );
2010-09-12 11:06:26 +02:00
stmt -> setString ( 0 , _login );
LoginDatabase . Execute ( stmt );
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_GET_FAILEDLOGINS );
stmt -> setString ( 0 , _login );
2010-06-06 23:08:23 +02:00
2010-09-12 11:06:26 +02:00
if ( PreparedQueryResult loginfail = LoginDatabase . Query ( stmt ))
2010-06-06 23:08:23 +02:00
{
2010-09-24 22:16:21 +02:00
uint32 failed_logins = ( * loginfail )[ 1 ]. GetUInt32 ();
2010-06-06 23:08:23 +02:00
if ( failed_logins >= MaxWrongPassCount )
{
2011-09-28 13:00:43 +02:00
uint32 WrongPassBanTime = ConfigMgr :: GetIntDefault ( "WrongPass.BanTime" , 600 );
bool WrongPassBanType = ConfigMgr :: GetBoolDefault ( "WrongPass.BanType" , false );
2010-06-06 23:08:23 +02:00
if ( WrongPassBanType )
{
2010-09-24 22:16:21 +02:00
uint32 acc_id = ( * loginfail )[ 0 ]. GetUInt32 ();
2010-09-12 11:06:26 +02:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SET_ACCAUTOBANNED );
stmt -> setUInt32 ( 0 , acc_id );
stmt -> setUInt32 ( 1 , WrongPassBanTime );
LoginDatabase . Execute ( stmt );
2010-12-23 23:25:44 +01:00
sLog -> outBasic ( "[AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times" ,
2010-06-06 23:08:23 +02:00
_login . c_str (), WrongPassBanTime , failed_logins );
}
else
{
2010-09-12 11:06:26 +02:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SET_IPAUTOBANNED );
stmt -> setString ( 0 , socket (). get_remote_address ());
stmt -> setUInt32 ( 1 , WrongPassBanTime );
LoginDatabase . Execute ( stmt );
2010-12-27 09:02:02 -08:00
sLog -> outBasic ( "[AuthChallenge] IP %s got banned for '%u' seconds because account %s failed to authenticate '%u' times" , socket (). get_remote_address (). c_str (), WrongPassBanTime , _login . c_str (), failed_logins );
2010-06-06 23:08:23 +02:00
}
}
}
}
}
return true ;
}
2010-11-16 14:29:01 +01:00
// Reconnect Challenge command handler
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleReconnectChallenge ()
{
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "Entering _HandleReconnectChallenge" );
2010-06-06 23:08:23 +02:00
if ( socket (). recv_len () < sizeof ( sAuthLogonChallenge_C ))
return false ;
2010-11-16 14:29:01 +01:00
// Read the first 4 bytes (header) to get the length of the remaining of the packet
2010-06-06 23:08:23 +02:00
std :: vector < uint8 > buf ;
buf . resize ( 4 );
socket (). recv (( char * ) & buf [ 0 ], 4 );
2010-12-04 11:19:41 -08:00
#if TRINITY_ENDIAN == TRINITY_BIGENDIAN
2010-06-06 23:08:23 +02:00
EndianConvert ( * (( uint16 * )( buf [ 0 ])));
2010-12-04 11:19:41 -08:00
#endif
2010-06-06 23:08:23 +02:00
uint16 remaining = (( sAuthLogonChallenge_C * ) & buf [ 0 ]) -> size ;
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "[ReconnectChallenge] got header, body is %#04x bytes" , remaining );
2010-06-06 23:08:23 +02:00
if (( remaining < sizeof ( sAuthLogonChallenge_C ) - buf . size ()) || ( socket (). recv_len () < remaining ))
return false ;
2010-11-16 14:29:01 +01:00
// No big fear of memory outage (size is int16, i.e. < 65536)
2010-06-06 23:08:23 +02:00
buf . resize ( remaining + buf . size () + 1 );
buf [ buf . size () - 1 ] = 0 ;
sAuthLogonChallenge_C * ch = ( sAuthLogonChallenge_C * ) & buf [ 0 ];
2010-11-16 14:29:01 +01:00
// Read the remaining of the packet
2010-06-06 23:08:23 +02:00
socket (). recv (( char * ) & buf [ 4 ], remaining );
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "[ReconnectChallenge] got full packet, %#04x bytes" , ch -> size );
sLog -> outStaticDebug ( "[ReconnectChallenge] name(%d): '%s'" , ch -> I_len , ch -> I );
2010-06-06 23:08:23 +02:00
_login = ( const char * ) ch -> I ;
2010-09-12 11:06:26 +02:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_GET_SESSIONKEY );
stmt -> setString ( 0 , _login );
PreparedQueryResult result = LoginDatabase . Query ( stmt );
2010-06-06 23:08:23 +02:00
// Stop if the account is not found
if ( ! result )
{
2010-12-23 23:25:44 +01:00
sLog -> outError ( "[ERROR] user %s tried to login and we cannot find his session key in the database." , _login . c_str ());
2010-06-06 23:08:23 +02:00
socket (). shutdown ();
return false ;
}
2010-12-21 05:50:19 +01:00
// Reinitialize build, expansion and the account securitylevel
_build = ch -> build ;
_expversion = ( AuthHelper :: IsPostBCAcceptedClientBuild ( _build ) ? POST_BC_EXP_FLAG : NO_VALID_EXP_FLAG ) | ( AuthHelper :: IsPreBCAcceptedClientBuild ( _build ) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG );
2010-12-21 05:57:53 +01:00
Field * fields = result -> Fetch ();
2010-12-21 05:50:19 +01:00
uint8 secLevel = fields [ 2 ]. GetUInt8 ();
_accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes ( secLevel ) : SEC_ADMINISTRATOR ;
2010-09-24 22:16:21 +02:00
K . SetHexStr (( * result )[ 0 ]. GetCString ());
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Sending response
2010-06-06 23:08:23 +02:00
ByteBuffer pkt ;
2010-12-04 11:19:41 -08:00
pkt << ( uint8 ) AUTH_RECONNECT_CHALLENGE ;
pkt << ( uint8 ) 0x00 ;
2010-06-06 23:08:23 +02:00
_reconnectProof . SetRand ( 16 * 8 );
2010-11-16 14:29:01 +01:00
pkt . append ( _reconnectProof . AsByteArray ( 16 ), 16 ); // 16 bytes random
2010-12-04 11:19:41 -08:00
pkt << ( uint64 ) 0x00 << ( uint64 ) 0x00 ; // 16 bytes zeros
2010-06-06 23:08:23 +02:00
socket (). send (( char const * ) pkt . contents (), pkt . size ());
return true ;
}
2010-11-16 14:29:01 +01:00
// Reconnect Proof command handler
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleReconnectProof ()
{
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "Entering _HandleReconnectProof" );
2010-11-16 14:29:01 +01:00
// Read the packet
2010-06-06 23:08:23 +02:00
sAuthReconnectProof_C lp ;
if ( ! socket (). recv (( char * ) & lp , sizeof ( sAuthReconnectProof_C )))
return false ;
if ( _login . empty () || ! _reconnectProof . GetNumBytes () || ! K . GetNumBytes ())
return false ;
BigNumber t1 ;
t1 . SetBinary ( lp . R1 , 16 );
2010-08-08 04:49:04 +02:00
SHA1Hash sha ;
2010-06-06 23:08:23 +02:00
sha . Initialize ();
sha . UpdateData ( _login );
sha . UpdateBigNumbers ( & t1 , & _reconnectProof , & K , NULL );
sha . Finalize ();
if ( ! memcmp ( sha . GetDigest (), lp . R2 , SHA_DIGEST_LENGTH ))
{
2010-11-16 14:29:01 +01:00
// Sending response
2010-06-06 23:08:23 +02:00
ByteBuffer pkt ;
2010-12-04 11:19:41 -08:00
pkt << ( uint8 ) AUTH_RECONNECT_PROOF ;
pkt << ( uint8 ) 0x00 ;
pkt << ( uint16 ) 0x00 ; // 2 bytes zeros
2010-06-06 23:08:23 +02:00
socket (). send (( char const * ) pkt . contents (), pkt . size ());
_authed = true ;
return true ;
}
else
{
2010-12-23 23:25:44 +01:00
sLog -> outError ( "[ERROR] user %s tried to login, but session invalid." , _login . c_str ());
2010-06-06 23:08:23 +02:00
socket (). shutdown ();
return false ;
}
}
2010-11-16 14:29:01 +01:00
// Realm List command handler
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleRealmList ()
{
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "Entering _HandleRealmList" );
2010-06-06 23:08:23 +02:00
if ( socket (). recv_len () < 5 )
return false ;
socket (). recv_skip ( 5 );
2010-11-16 14:29:01 +01:00
// Get the user id (else close the connection)
2010-09-12 11:06:26 +02:00
// No SQL injection (prepared statement)
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_GET_ACCIDBYNAME );
stmt -> setString ( 0 , _login );
PreparedQueryResult result = LoginDatabase . Query ( stmt );
2010-06-06 23:08:23 +02:00
if ( ! result )
{
2010-12-23 23:25:44 +01:00
sLog -> outError ( "[ERROR] user %s tried to login and we cannot find him in the database." , _login . c_str ());
2010-06-06 23:08:23 +02:00
socket (). shutdown ();
return false ;
}
2010-09-24 22:16:21 +02:00
Field * fields = result -> Fetch ();
uint32 id = fields [ 0 ]. GetUInt32 ();
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Update realm list if need
2010-06-06 23:08:23 +02:00
sRealmList -> UpdateIfNeed ();
2010-11-16 14:29:01 +01:00
// Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm)
2010-06-06 23:08:23 +02:00
ByteBuffer pkt ;
size_t RealmListSize = 0 ;
for ( RealmList :: RealmMap :: const_iterator i = sRealmList -> begin (); i != sRealmList -> end (); ++ i )
{
// don't work with realms which not compatible with the client
2010-12-04 11:19:41 -08:00
if (( _expversion & POST_BC_EXP_FLAG ) && i -> second . gamebuild != _build )
continue ;
else if (( _expversion & PRE_BC_EXP_FLAG ) && ! AuthHelper :: IsPreBCAcceptedClientBuild ( i -> second . gamebuild ))
2010-06-06 23:08:23 +02:00
continue ;
uint8 AmountOfCharacters ;
// No SQL injection. id of realm is controlled by the database.
2010-09-12 11:06:26 +02:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_GET_NUMCHARSONREALM );
stmt -> setUInt32 ( 0 , i -> second . m_ID );
stmt -> setUInt32 ( 1 , id );
result = LoginDatabase . Query ( stmt );
2010-06-06 23:08:23 +02:00
if ( result )
2010-09-24 22:16:21 +02:00
AmountOfCharacters = ( * result )[ 0 ]. GetUInt8 ();
2010-06-06 23:08:23 +02:00
else
AmountOfCharacters = 0 ;
uint8 lock = ( i -> second . allowedSecurityLevel > _accountSecurityLevel ) ? 1 : 0 ;
2010-11-16 14:29:01 +01:00
pkt << i -> second . icon ; // realm type
2010-12-04 11:19:41 -08:00
if ( _expversion & POST_BC_EXP_FLAG ) // only 2.x and 3.x clients
2010-11-16 14:29:01 +01:00
pkt << lock ; // if 1, then realm locked
pkt << i -> second . color ; // if 2, then realm is offline
2010-06-06 23:08:23 +02:00
pkt << i -> first ;
pkt << i -> second . address ;
pkt << i -> second . populationLevel ;
pkt << AmountOfCharacters ;
pkt << i -> second . timezone ; // realm category
2010-12-27 09:02:02 -08:00
if ( _expversion & POST_BC_EXP_FLAG ) // 2.x and 3.x clients
2010-12-04 11:19:41 -08:00
pkt << ( uint8 ) 0x2C ; // unk, may be realm number/id?
2010-06-06 23:08:23 +02:00
else
2010-12-04 11:19:41 -08:00
pkt << ( uint8 ) 0x0 ; // 1.12.1 and 1.12.2 clients
2010-06-06 23:08:23 +02:00
++ RealmListSize ;
}
2010-12-04 11:19:41 -08:00
if ( _expversion & POST_BC_EXP_FLAG ) // 2.x and 3.x clients
2010-06-06 23:08:23 +02:00
{
2010-12-04 11:19:41 -08:00
pkt << ( uint8 ) 0x10 ;
pkt << ( uint8 ) 0x00 ;
2010-11-16 14:29:01 +01:00
}
else // 1.12.1 and 1.12.2 clients
{
2010-12-04 11:19:41 -08:00
pkt << ( uint8 ) 0x00 ;
pkt << ( uint8 ) 0x02 ;
2010-06-06 23:08:23 +02:00
}
// make a ByteBuffer which stores the RealmList's size
ByteBuffer RealmListSizeBuffer ;
RealmListSizeBuffer << ( uint32 ) 0 ;
2010-12-04 11:19:41 -08:00
if ( _expversion & POST_BC_EXP_FLAG ) // only 2.x and 3.x clients
2010-06-06 23:08:23 +02:00
RealmListSizeBuffer << ( uint16 ) RealmListSize ;
else
RealmListSizeBuffer << ( uint32 ) RealmListSize ;
ByteBuffer hdr ;
hdr << ( uint8 ) REALM_LIST ;
hdr << ( uint16 )( pkt . size () + RealmListSizeBuffer . size ());
2010-11-16 14:29:01 +01:00
hdr . append ( RealmListSizeBuffer ); // append RealmList's size buffer
hdr . append ( pkt ); // append realms in the realmlist
2010-06-06 23:08:23 +02:00
socket (). send (( char const * ) hdr . contents (), hdr . size ());
return true ;
}
2010-11-16 14:29:01 +01:00
// Resume patch transfer
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleXferResume ()
{
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "Entering _HandleXferResume" );
2010-11-16 14:29:01 +01:00
// Check packet length and patch existence
2010-06-06 23:08:23 +02:00
if ( socket (). recv_len () < 9 || ! pPatch )
{
2010-12-23 23:25:44 +01:00
sLog -> outError ( "Error while resuming patch transfer (wrong packet)" );
2010-06-06 23:08:23 +02:00
return false ;
}
2010-11-16 14:29:01 +01:00
// Launch a PatcherRunnable thread starting at given patch file offset
2010-06-06 23:08:23 +02:00
uint64 start ;
socket (). recv_skip ( 1 );
2011-04-29 20:47:02 +02:00
socket (). recv (( char * ) & start , sizeof ( start ));
2010-08-29 20:28:14 -07:00
fseek ( pPatch , long ( start ), 0 );
2010-06-06 23:08:23 +02:00
ACE_Based :: Thread u ( new PatcherRunnable ( this ));
return true ;
}
2010-11-16 14:29:01 +01:00
// Cancel patch transfer
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleXferCancel ()
{
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "Entering _HandleXferCancel" );
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Close and delete the socket
2010-06-06 23:08:23 +02:00
socket (). recv_skip ( 1 ); //clear input buffer
socket (). shutdown ();
return true ;
}
2010-11-16 14:29:01 +01:00
// Accept patch transfer
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleXferAccept ()
{
2010-12-23 23:25:44 +01:00
sLog -> outStaticDebug ( "Entering _HandleXferAccept" );
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Check packet length and patch existence
2010-06-06 23:08:23 +02:00
if ( ! pPatch )
{
2010-12-23 23:25:44 +01:00
sLog -> outError ( "Error while accepting patch transfer (wrong packet)" );
2010-06-06 23:08:23 +02:00
return false ;
}
2010-11-16 14:29:01 +01:00
// Launch a PatcherRunnable thread, starting at the beginning of the patch file
2010-06-06 23:08:23 +02:00
socket (). recv_skip ( 1 ); // clear input buffer
fseek ( pPatch , 0 , 0 );
ACE_Based :: Thread u ( new PatcherRunnable ( this ));
return true ;
}
2011-09-15 14:08:17 +02:00
PatcherRunnable :: PatcherRunnable ( class AuthSocket * as )
2010-06-06 23:08:23 +02:00
{
mySocket = as ;
}
2010-11-16 14:29:01 +01:00
// Send content of patch file to the client
void PatcherRunnable :: run () {}
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Preload MD5 hashes of existing patch files on server
2010-06-06 23:08:23 +02:00
#ifndef _WIN32
#include <dirent.h>
#include <errno.h>
void Patcher :: LoadPatchesInfo ()
{
2010-12-27 09:02:02 -08:00
DIR * dirp ;
struct dirent * dp ;
2010-06-06 23:08:23 +02:00
dirp = opendir ( "./patches/" );
2010-12-04 11:19:41 -08:00
2010-06-06 23:08:23 +02:00
if ( ! dirp )
return ;
2010-12-04 11:19:41 -08:00
2010-06-06 23:08:23 +02:00
while ( dirp )
{
errno = 0 ;
if (( dp = readdir ( dirp )) != NULL )
{
int l = strlen ( dp -> d_name );
2010-12-27 09:02:02 -08:00
2010-06-06 23:08:23 +02:00
if ( l < 8 )
continue ;
2010-12-27 09:02:02 -08:00
2010-12-04 11:19:41 -08:00
if ( ! memcmp ( & dp -> d_name [ l - 4 ], ".mpq" , 4 ))
2010-06-06 23:08:23 +02:00
LoadPatchMD5 ( dp -> d_name );
}
else
{
if ( errno != 0 )
{
closedir ( dirp );
return ;
}
break ;
}
}
if ( dirp )
closedir ( dirp );
}
#else
void Patcher :: LoadPatchesInfo ()
{
WIN32_FIND_DATA fil ;
2010-12-27 09:02:02 -08:00
HANDLE hFil = FindFirstFile ( "./patches/*.mpq" , & fil );
2010-06-06 23:08:23 +02:00
if ( hFil == INVALID_HANDLE_VALUE )
return ; // no patches were found
do
LoadPatchMD5 ( fil . cFileName );
2010-12-04 11:19:41 -08:00
while ( FindNextFile ( hFil , & fil ));
2010-06-06 23:08:23 +02:00
}
#endif
2010-11-16 14:29:01 +01:00
// Calculate and store MD5 hash for a given patch file
2010-12-04 11:19:41 -08:00
void Patcher :: LoadPatchMD5 ( char * szFileName )
2010-06-06 23:08:23 +02:00
{
2010-11-16 14:29:01 +01:00
// Try to open the patch file
2010-06-06 23:08:23 +02:00
std :: string path = "./patches/" ;
path += szFileName ;
2011-09-15 14:08:17 +02:00
FILE * pPatch = fopen ( path . c_str (), "rb" );
2011-02-20 20:16:34 +01:00
sLog -> outDebug ( LOG_FILTER_NETWORKIO , "Loading patch info from %s \n " , path . c_str ());
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
if ( ! pPatch )
{
2010-12-23 23:25:44 +01:00
sLog -> outError ( "Error loading patch %s \n " , path . c_str ());
2010-06-06 23:08:23 +02:00
return ;
}
2010-11-16 14:29:01 +01:00
// Calculate the MD5 hash
2010-06-06 23:08:23 +02:00
MD5_CTX ctx ;
MD5_Init ( & ctx );
2010-12-04 11:19:41 -08:00
uint8 * buf = new uint8 [ 512 * 1024 ];
2010-06-06 23:08:23 +02:00
while ( ! feof ( pPatch ))
{
2010-12-04 11:19:41 -08:00
size_t read = fread ( buf , 1 , 512 * 1024 , pPatch );
2010-06-06 23:08:23 +02:00
MD5_Update ( & ctx , buf , read );
}
2010-12-04 11:19:41 -08:00
2010-06-06 23:08:23 +02:00
delete [] buf ;
fclose ( pPatch );
2010-11-16 14:29:01 +01:00
// Store the result in the internal patch hash map
2010-06-06 23:08:23 +02:00
_patches [ path ] = new PATCH_INFO ;
MD5_Final (( uint8 * ) & _patches [ path ] -> md5 , & ctx );
}
2010-11-16 14:29:01 +01:00
// Get cached MD5 hash for a given patch file
2010-06-06 23:08:23 +02:00
bool Patcher :: GetHash ( char * pat , uint8 mymd5 [ 16 ])
{
for ( Patches :: iterator i = _patches . begin (); i != _patches . end (); ++ i )
if ( ! stricmp ( pat , i -> first . c_str ()))
2010-12-04 11:19:41 -08:00
{
memcpy ( mymd5 , i -> second -> md5 , 16 );
return true ;
}
2010-06-06 23:08:23 +02:00
return false ;
}
2010-11-16 14:29:01 +01:00
// Launch the patch hashing mechanism on object creation
2010-06-06 23:08:23 +02:00
Patcher :: Patcher ()
{
LoadPatchesInfo ();
}
2010-11-16 14:29:01 +01:00
// Empty and delete the patch map on termination
2010-06-06 23:08:23 +02:00
Patcher ::~ Patcher ()
{
for ( Patches :: iterator i = _patches . begin (); i != _patches . end (); ++ i )
delete i -> second ;
}