2014-04-30 20:16:08 +02:00
/*
2015-01-01 00:27:46 +01:00
* Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
2014-04-30 20:16:08 +02:00
*
* 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/>.
*/
#include "AuthCodes.h"
2014-10-10 23:19:40 +02:00
#include "BitStream.h"
2014-10-12 20:58:25 +02:00
#include "PacketManager.h"
2014-10-10 23:19:40 +02:00
#include "SessionManager.h"
2014-05-06 23:43:29 +02:00
#include "Database/DatabaseEnv.h"
#include "HmacHash.h"
#include "Log.h"
#include "RealmList.h"
#include "SHA256.h"
2014-04-30 20:16:08 +02:00
#include <map>
2014-05-06 23:43:29 +02:00
2014-07-19 17:03:32 +02:00
Battlenet :: Session :: ModuleHandler const Battlenet :: Session :: ModuleHandlers [ MODULE_COUNT ] =
2014-05-06 23:43:29 +02:00
{
2014-07-19 17:03:32 +02:00
& Battlenet :: Session :: HandlePasswordModule ,
& Battlenet :: Session :: UnhandledModule ,
& Battlenet :: Session :: UnhandledModule ,
& Battlenet :: Session :: HandleSelectGameAccountModule ,
& Battlenet :: Session :: HandleRiskFingerprintModule ,
& Battlenet :: Session :: HandleResumeModule ,
2014-05-06 23:43:29 +02:00
};
2014-09-12 22:09:07 +02:00
Battlenet :: Session :: Session ( tcp :: socket && socket ) : Socket ( std :: move ( socket )), _accountId ( 0 ), _accountName (), _locale (),
2014-08-22 20:09:18 +02:00
_os (), _build ( 0 ), _gameAccountId ( 0 ), _gameAccountName (), _accountSecurityLevel ( SEC_PLAYER ), I (), s (), v (), b (), B (), K (),
2014-12-24 13:48:52 +01:00
_reconnectProof (), _crypt (), _authed ( false ), _subscribedToRealmListUpdates ( false ), _toonOnline ( false )
2014-05-06 23:43:29 +02:00
{
static uint8 const N_Bytes [] =
{
0xAB , 0x24 , 0x43 , 0x63 , 0xA9 , 0xC2 , 0xA6 , 0xC3 , 0x3B , 0x37 , 0xE4 , 0x61 , 0x84 , 0x25 , 0x9F , 0x8B ,
0x3F , 0xCB , 0x8A , 0x85 , 0x27 , 0xFC , 0x3D , 0x87 , 0xBE , 0xA0 , 0x54 , 0xD2 , 0x38 , 0x5D , 0x12 , 0xB7 ,
0x61 , 0x44 , 0x2E , 0x83 , 0xFA , 0xC2 , 0x21 , 0xD9 , 0x10 , 0x9F , 0xC1 , 0x9F , 0xEA , 0x50 , 0xE3 , 0x09 ,
0xA6 , 0xE5 , 0x5E , 0x23 , 0xA7 , 0x77 , 0xEB , 0x00 , 0xC7 , 0xBA , 0xBF , 0xF8 , 0x55 , 0x8A , 0x0E , 0x80 ,
0x2B , 0x14 , 0x1A , 0xA2 , 0xD4 , 0x43 , 0xA9 , 0xD4 , 0xAF , 0xAD , 0xB5 , 0xE1 , 0xF5 , 0xAC , 0xA6 , 0x13 ,
0x1C , 0x69 , 0x78 , 0x64 , 0x0B , 0x7B , 0xAF , 0x9C , 0xC5 , 0x50 , 0x31 , 0x8A , 0x23 , 0x08 , 0x01 , 0xA1 ,
0xF5 , 0xFE , 0x31 , 0x32 , 0x7F , 0xE2 , 0x05 , 0x82 , 0xD6 , 0x0B , 0xED , 0x4D , 0x55 , 0x32 , 0x41 , 0x94 ,
0x29 , 0x6F , 0x55 , 0x7D , 0xE3 , 0x0F , 0x77 , 0x19 , 0xE5 , 0x6C , 0x30 , 0xEB , 0xDE , 0xF6 , 0xA7 , 0x86
};
N . SetBinary ( N_Bytes , sizeof ( N_Bytes ));
g . SetDword ( 2 );
SHA256Hash sha ;
sha . UpdateBigNumbers ( & N , & g , NULL );
sha . Finalize ();
k . SetBinary ( sha . GetDigest (), sha . GetLength ());
}
2014-07-19 17:03:32 +02:00
Battlenet :: Session ::~ Session ()
{
2014-10-10 23:19:40 +02:00
sSessionMgr . RemoveSession ( this );
2014-07-19 17:03:32 +02:00
}
void Battlenet :: Session :: _SetVSFields ( std :: string const & pstr )
2014-05-06 23:43:29 +02:00
{
2014-07-19 17:03:32 +02:00
s . SetRand ( uint32 ( BufferSizes :: SRP_6_S ) * 8 );
2014-05-06 23:43:29 +02:00
BigNumber p ;
p . SetHexStr ( pstr . c_str ());
SHA256Hash sha ;
sha . UpdateBigNumbers ( & s , & p , NULL );
sha . Finalize ();
BigNumber x ;
x . SetBinary ( sha . GetDigest (), sha . GetLength ());
v = g . ModExp ( x , N );
2014-06-01 13:03:30 +02:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_BNET_VS_FIELDS );
stmt -> setString ( 0 , v . AsHexStr ());
stmt -> setString ( 1 , s . AsHexStr ());
stmt -> setString ( 2 , _accountName );
2014-05-06 23:43:29 +02:00
2014-06-01 13:03:30 +02:00
LoginDatabase . Execute ( stmt );
2014-05-06 23:43:29 +02:00
}
2014-10-12 20:58:25 +02:00
void Battlenet :: Session :: LogUnhandledPacket ( PacketHeader const & header )
2014-10-05 16:21:27 +02:00
{
2014-10-12 20:58:25 +02:00
TC_LOG_DEBUG ( "session.packets" , "%s Received unhandled packet %s" , GetClientInfo (). c_str (), sPacketManager . GetClientPacketName ( header ));
2014-10-05 16:21:27 +02:00
}
2014-10-18 17:03:30 +02:00
void Battlenet :: Session :: HandleLogonRequest ( Authentication :: LogonRequest3 const & logonRequest )
2014-04-30 20:16:08 +02:00
{
2014-05-06 23:43:29 +02:00
// Verify that this IP is not in the ip_banned table
LoginDatabase . Execute ( LoginDatabase . GetPreparedStatement ( LOGIN_DEL_EXPIRED_IP_BANS ));
2014-07-27 21:50:01 +02:00
std :: string ip_address = GetRemoteIpAddress (). to_string ();
2014-05-06 23:43:29 +02:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_IP_BANNED );
stmt -> setString ( 0 , ip_address );
if ( PreparedQueryResult result = LoginDatabase . Query ( stmt ))
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( LOGIN_BANNED );
AsyncWrite ( logonResponse );
2014-10-12 15:34:01 +02:00
TC_LOG_DEBUG ( "session" , "[Battlenet::LogonRequest] Banned ip '%s:%d' tries to login!" , ip_address . c_str (), GetRemotePort ());
2014-10-05 16:21:27 +02:00
return ;
2014-05-06 23:43:29 +02:00
}
2014-10-10 23:48:01 +02:00
if ( logonRequest . Program != "WoW" )
2014-05-02 02:55:10 +02:00
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( AUTH_INVALID_PROGRAM );
AsyncWrite ( logonResponse );
2014-11-09 19:18:54 +01:00
TC_LOG_DEBUG ( "session" , "[Battlenet::LogonRequest] %s attempted to log in with game other than WoW (using %s)!" , GetClientInfo (). c_str (), logonRequest . Program . c_str ());
2014-10-05 16:21:27 +02:00
return ;
2014-05-02 02:55:10 +02:00
}
2014-10-10 23:48:01 +02:00
if ( ! sComponentMgr -> HasPlatform ( logonRequest . Platform ))
2014-05-02 02:55:10 +02:00
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( AUTH_INVALID_OS );
AsyncWrite ( logonResponse );
2014-11-09 19:18:54 +01:00
TC_LOG_DEBUG ( "session" , "[Battlenet::LogonRequest] %s attempted to log in from an unsupported platform (using %s)!" , GetClientInfo (). c_str (), logonRequest . Platform . c_str ());
2014-10-05 16:21:27 +02:00
return ;
2014-05-02 02:55:10 +02:00
}
2014-10-10 23:48:01 +02:00
if ( ! sComponentMgr -> HasPlatform ( logonRequest . Locale ))
2014-05-02 02:55:10 +02:00
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( AUTH_UNSUPPORTED_LANGUAGE );
AsyncWrite ( logonResponse );
2014-11-09 19:18:54 +01:00
TC_LOG_DEBUG ( "session" , "[Battlenet::LogonRequest] %s attempted to log in with unsupported locale (using %s)!" , GetClientInfo (). c_str (), logonRequest . Locale . c_str ());
2014-10-05 16:21:27 +02:00
return ;
2014-05-02 02:55:10 +02:00
}
2014-10-10 23:48:01 +02:00
for ( Component const & component : logonRequest . Components )
2014-04-30 23:02:01 +02:00
{
2014-10-10 23:19:40 +02:00
if ( ! sComponentMgr -> HasComponent ( & component ))
2014-04-30 23:02:01 +02:00
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
2014-10-10 23:19:40 +02:00
if ( ! sComponentMgr -> HasProgram ( component . Program ))
2014-11-09 19:18:54 +01:00
{
2014-10-10 23:48:01 +02:00
logonResponse -> SetAuthResult ( AUTH_INVALID_PROGRAM );
2014-11-09 19:18:54 +01:00
TC_LOG_DEBUG ( "session" , "[Battlenet::LogonRequest] %s is using unsupported component program %s!" , GetClientInfo (). c_str (), component . Program . c_str ());
}
2014-10-10 23:19:40 +02:00
else if ( ! sComponentMgr -> HasPlatform ( component . Platform ))
2014-11-09 19:18:54 +01:00
{
2014-10-10 23:48:01 +02:00
logonResponse -> SetAuthResult ( AUTH_INVALID_OS );
2014-11-09 19:18:54 +01:00
TC_LOG_DEBUG ( "session" , "[Battlenet::LogonRequest] %s is using unsupported component platform %s!" , GetClientInfo (). c_str (), component . Platform . c_str ());
}
2014-05-02 02:55:10 +02:00
else
2014-06-08 15:34:24 +02:00
{
if ( component . Program != "WoW" || AuthHelper :: IsBuildSupportingBattlenet ( component . Build ))
2014-10-10 23:48:01 +02:00
logonResponse -> SetAuthResult ( AUTH_REGION_BAD_VERSION );
2014-06-08 15:34:24 +02:00
else
2014-10-10 23:48:01 +02:00
logonResponse -> SetAuthResult ( AUTH_USE_GRUNT_LOGON );
2014-11-09 19:18:54 +01:00
TC_LOG_DEBUG ( "session" , "[Battlenet::LogonRequest] %s is using unsupported component version %u!" , GetClientInfo (). c_str (), component . Build );
2014-06-08 15:34:24 +02:00
}
2014-04-30 23:02:01 +02:00
2014-10-10 23:48:01 +02:00
AsyncWrite ( logonResponse );
2014-10-05 16:21:27 +02:00
return ;
2014-04-30 23:02:01 +02:00
}
2014-05-06 23:43:29 +02:00
if ( component . Platform == "base" )
_build = component . Build ;
2014-04-30 23:02:01 +02:00
}
2014-10-10 23:48:01 +02:00
_accountName = logonRequest . Login ;
_locale = logonRequest . Locale ;
_os = logonRequest . Platform ;
2014-04-30 20:16:08 +02:00
2014-05-06 23:43:29 +02:00
Utf8ToUpperOnlyLatin ( _accountName );
2014-05-13 22:02:51 +02:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_BNET_ACCOUNT_INFO );
stmt -> setString ( 0 , _accountName );
PreparedQueryResult result = LoginDatabase . Query ( stmt );
2014-05-06 23:43:29 +02:00
if ( ! result )
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( AUTH_UNKNOWN_ACCOUNT );
AsyncWrite ( logonResponse );
2014-11-09 19:18:54 +01:00
TC_LOG_DEBUG ( "session" , "[Battlenet::LogonRequest] %s is trying to log in from unknown account!" , GetClientInfo (). c_str ());
2014-10-05 16:21:27 +02:00
return ;
2014-05-06 23:43:29 +02:00
}
Field * fields = result -> Fetch ();
_accountId = fields [ 1 ]. GetUInt32 ();
// If the IP is 'locked', check that the player comes indeed from the correct IP address
if ( fields [ 2 ]. GetUInt8 () == 1 ) // if ip is locked
{
2014-10-12 15:34:01 +02:00
TC_LOG_DEBUG ( "session" , "[Battlenet::LogonRequest] Account '%s' is locked to IP - '%s' is logging in from '%s'" , _accountName . c_str (), fields [ 4 ]. GetCString (), ip_address . c_str ());
2014-05-06 23:43:29 +02:00
if ( strcmp ( fields [ 4 ]. GetCString (), ip_address . c_str ()) != 0 )
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( AUTH_ACCOUNT_LOCKED );
AsyncWrite ( logonResponse );
2014-10-05 16:21:27 +02:00
return ;
2014-05-06 23:43:29 +02:00
}
}
else
{
2014-10-12 15:34:01 +02:00
TC_LOG_DEBUG ( "session" , "[Battlenet::LogonRequest] Account '%s' is not locked to ip" , _accountName . c_str ());
2014-05-06 23:43:29 +02:00
std :: string accountCountry = fields [ 3 ]. GetString ();
if ( accountCountry . empty () || accountCountry == "00" )
2014-10-12 15:34:01 +02:00
TC_LOG_DEBUG ( "session" , "[Battlenet::LogonRequest] Account '%s' is not locked to country" , _accountName . c_str ());
2014-05-06 23:43:29 +02:00
else if ( ! accountCountry . empty ())
{
uint32 ip = inet_addr ( ip_address . c_str ());
EndianConvertReverse ( ip );
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_LOGON_COUNTRY );
stmt -> setUInt32 ( 0 , ip );
if ( PreparedQueryResult sessionCountryQuery = LoginDatabase . Query ( stmt ))
{
std :: string loginCountry = ( * sessionCountryQuery )[ 0 ]. GetString ();
2014-10-12 15:34:01 +02:00
TC_LOG_DEBUG ( "session" , "[Battlenet::LogonRequest] Account '%s' is locked to country: '%s' Player country is '%s'" , _accountName . c_str (), accountCountry . c_str (), loginCountry . c_str ());
2014-05-06 23:43:29 +02:00
if ( loginCountry != accountCountry )
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( AUTH_ACCOUNT_LOCKED );
AsyncWrite ( logonResponse );
2014-10-05 16:21:27 +02:00
return ;
2014-05-06 23:43:29 +02:00
}
}
}
}
//set expired bans to inactive
2014-05-13 22:02:51 +02:00
LoginDatabase . DirectExecute ( LoginDatabase . GetPreparedStatement ( LOGIN_DEL_BNET_EXPIRED_BANS ));
2014-05-06 23:43:29 +02:00
// If the account is banned, reject the logon attempt
2014-05-13 22:02:51 +02:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_BNET_ACTIVE_ACCOUNT_BAN );
stmt -> setUInt32 ( 0 , _accountId );
PreparedQueryResult banresult = LoginDatabase . Query ( stmt );
2014-05-06 23:43:29 +02:00
if ( banresult )
{
Field * fields = banresult -> Fetch ();
if ( fields [ 0 ]. GetUInt32 () == fields [ 1 ]. GetUInt32 ())
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( LOGIN_BANNED );
AsyncWrite ( logonResponse );
2014-10-12 15:34:01 +02:00
TC_LOG_DEBUG ( "session" , "'%s:%d' [Battlenet::LogonRequest] Banned account %s tried to login!" , ip_address . c_str (), GetRemotePort (), _accountName . c_str ());
2014-10-05 16:21:27 +02:00
return ;
2014-05-06 23:43:29 +02:00
}
else
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( LOGIN_SUSPENDED );
AsyncWrite ( logonResponse );
2014-10-12 15:34:01 +02:00
TC_LOG_DEBUG ( "session" , "'%s:%d' [Battlenet::LogonRequest] Temporarily banned account %s tried to login!" , ip_address . c_str (), GetRemotePort (), _accountName . c_str ());
2014-10-05 16:21:27 +02:00
return ;
2014-05-06 23:43:29 +02:00
}
}
SHA256Hash sha ;
sha . UpdateData ( _accountName );
sha . Finalize ();
I . SetBinary ( sha . GetDigest (), sha . GetLength ());
2014-10-10 23:19:40 +02:00
ModuleInfo * password = sModuleMgr -> CreateModule ( _os , "Password" );
ModuleInfo * thumbprint = sModuleMgr -> CreateModule ( _os , "Thumbprint" );
2014-05-06 23:43:29 +02:00
std :: string pStr = fields [ 0 ]. GetString ();
std :: string databaseV = fields [ 5 ]. GetString ();
std :: string databaseS = fields [ 6 ]. GetString ();
2014-07-19 17:03:32 +02:00
if ( databaseV . size () != size_t ( BufferSizes :: SRP_6_V ) * 2 || databaseS . size () != size_t ( BufferSizes :: SRP_6_S ) * 2 )
2014-05-06 23:43:29 +02:00
_SetVSFields ( pStr );
else
{
s . SetHexStr ( databaseS . c_str ());
v . SetHexStr ( databaseV . c_str ());
}
b . SetRand ( 128 * 8 );
B = (( v * k ) + g . ModExp ( b , N )) % N ;
2014-06-03 19:15:12 +02:00
BigNumber unk ;
unk . SetRand ( 128 * 8 );
2014-05-06 23:43:29 +02:00
BitStream passwordData ;
uint8 state = 0 ;
passwordData . WriteBytes ( & state , 1 );
passwordData . WriteBytes ( I . AsByteArray ( 32 ). get (), 32 );
passwordData . WriteBytes ( s . AsByteArray ( 32 ). get (), 32 );
passwordData . WriteBytes ( B . AsByteArray ( 128 ). get (), 128 );
2014-06-03 19:15:12 +02:00
passwordData . WriteBytes ( unk . AsByteArray ( 128 ). get (), 128 );
2014-05-06 23:43:29 +02:00
password -> DataSize = passwordData . GetSize ();
password -> Data = new uint8 [ password -> DataSize ];
memcpy ( password -> Data , passwordData . GetBuffer (), password -> DataSize );
_modulesWaitingForData . push ( MODULE_PASSWORD );
2014-10-10 23:48:01 +02:00
Authentication :: ProofRequest * proofRequest = new Authentication :: ProofRequest ();
proofRequest -> Modules . push_back ( password );
2014-05-06 23:43:29 +02:00
// if has authenticator, send Token module
2014-10-10 23:48:01 +02:00
proofRequest -> Modules . push_back ( thumbprint );
AsyncWrite ( proofRequest );
2014-04-30 20:16:08 +02:00
}
2014-10-10 23:48:01 +02:00
void Battlenet :: Session :: HandleResumeRequest ( Authentication :: ResumeRequest const & resumeRequest )
2014-06-01 13:56:36 +02:00
{
2014-10-10 23:48:01 +02:00
_accountName = resumeRequest . Login ;
_locale = resumeRequest . Locale ;
_os = resumeRequest . Platform ;
auto baseComponent = std :: find_if ( resumeRequest . Components . begin (), resumeRequest . Components . end (), []( Component const & c ) { return c . Program == "base" ; });
if ( baseComponent != resumeRequest . Components . end ())
2014-06-01 13:56:36 +02:00
_build = baseComponent -> Build ;
Utf8ToUpperOnlyLatin ( _accountName );
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_BNET_RECONNECT_INFO );
stmt -> setString ( 0 , _accountName );
2014-10-10 23:48:01 +02:00
stmt -> setString ( 1 , resumeRequest . GameAccountName );
2014-06-01 13:56:36 +02:00
PreparedQueryResult result = LoginDatabase . Query ( stmt );
if ( ! result )
{
2014-10-10 23:48:01 +02:00
Authentication :: ResumeResponse * resumeResponse = new Authentication :: ResumeResponse ();
resumeResponse -> SetAuthResult ( AUTH_UNKNOWN_ACCOUNT );
AsyncWrite ( resumeResponse );
2014-10-05 16:21:27 +02:00
return ;
2014-06-01 13:56:36 +02:00
}
Field * fields = result -> Fetch ();
_accountId = fields [ 0 ]. GetUInt32 ();
K . SetHexStr ( fields [ 1 ]. GetString (). c_str ());
_gameAccountId = fields [ 2 ]. GetUInt32 ();
2014-10-10 23:48:01 +02:00
_gameAccountName = resumeRequest . GameAccountName ;
2014-06-01 13:56:36 +02:00
2014-10-10 23:19:40 +02:00
ModuleInfo * thumbprint = sModuleMgr -> CreateModule ( _os , "Thumbprint" );
ModuleInfo * resume = sModuleMgr -> CreateModule ( _os , "Resume" );
2014-06-01 13:56:36 +02:00
BitStream resumeData ;
uint8 state = 0 ;
_reconnectProof . SetRand ( 16 * 8 );
resumeData . WriteBytes ( & state , 1 );
resumeData . WriteBytes ( _reconnectProof . AsByteArray (). get (), 16 );
resume -> DataSize = resumeData . GetSize ();
resume -> Data = new uint8 [ resume -> DataSize ];
memcpy ( resume -> Data , resumeData . GetBuffer (), resume -> DataSize );
_modulesWaitingForData . push ( MODULE_RESUME );
2014-10-10 23:48:01 +02:00
Authentication :: ProofRequest * proofRequest = new Authentication :: ProofRequest ();
proofRequest -> Modules . push_back ( thumbprint );
proofRequest -> Modules . push_back ( resume );
AsyncWrite ( proofRequest );
2014-06-01 13:56:36 +02:00
}
2014-10-10 23:48:01 +02:00
void Battlenet :: Session :: HandleProofResponse ( Authentication :: ProofResponse const & proofResponse )
2014-04-30 20:16:08 +02:00
{
2014-10-10 23:48:01 +02:00
if ( _modulesWaitingForData . size () < proofResponse . Modules . size ())
2014-05-06 23:43:29 +02:00
{
2014-10-04 15:45:33 +02:00
Authentication :: LogonResponse * complete = new Authentication :: LogonResponse ();
2014-07-19 17:03:32 +02:00
complete -> SetAuthResult ( AUTH_CORRUPTED_MODULE );
AsyncWrite ( complete );
2014-10-05 16:21:27 +02:00
return ;
2014-05-06 23:43:29 +02:00
}
2014-04-30 20:16:08 +02:00
2014-05-06 23:43:29 +02:00
ServerPacket * response = nullptr ;
2014-10-10 23:48:01 +02:00
for ( size_t i = 0 ; i < proofResponse . Modules . size (); ++ i )
2014-05-06 23:43:29 +02:00
{
2014-10-10 23:48:01 +02:00
if ( ! ( this ->* ( ModuleHandlers [ _modulesWaitingForData . front ()]))( proofResponse . Modules [ i ], & response ))
2014-05-06 23:43:29 +02:00
break ;
_modulesWaitingForData . pop ();
}
if ( ! response )
{
2014-10-04 15:45:33 +02:00
response = new Authentication :: LogonResponse ();
static_cast < Authentication :: LogonResponse *> ( response ) -> SetAuthResult ( AUTH_INTERNAL_ERROR );
2014-05-06 23:43:29 +02:00
}
2014-07-19 17:03:32 +02:00
AsyncWrite ( response );
2014-10-05 16:21:27 +02:00
return ;
2014-04-30 20:16:08 +02:00
}
2014-10-05 16:21:27 +02:00
void Battlenet :: Session :: HandlePing ( Connection :: Ping const & /*ping*/ )
2014-04-30 20:16:08 +02:00
{
2014-10-04 15:45:33 +02:00
AsyncWrite ( new Connection :: Pong ());
2014-05-06 23:43:29 +02:00
}
2014-04-30 20:16:08 +02:00
2014-12-20 00:25:11 +01:00
void Battlenet :: Session :: HandleEnableEncryption ( Connection :: EnableEncryption & enableEncryption )
2014-05-06 23:43:29 +02:00
{
_crypt . Init ( & K );
2014-12-20 00:25:11 +01:00
_crypt . DecryptRecv ( enableEncryption . GetRemainingData (), enableEncryption . GetRemainingSize ());
2014-04-30 20:16:08 +02:00
}
2014-10-05 16:21:27 +02:00
void Battlenet :: Session :: HandleLogoutRequest ( Connection :: LogoutRequest const & /*logoutRequest*/ )
2014-06-01 13:56:36 +02:00
{
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_BNET_SESSION_KEY );
stmt -> setString ( 0 , "" );
2014-06-08 17:58:28 +02:00
stmt -> setBool ( 1 , false );
stmt -> setUInt32 ( 2 , _accountId );
2014-06-01 13:56:36 +02:00
LoginDatabase . Execute ( stmt );
}
2014-10-11 22:45:01 +02:00
void Battlenet :: Session :: HandleConnectionClosing ( Connection :: ConnectionClosing const & /*connectionClosing*/ )
2014-05-06 23:43:29 +02:00
{
2014-10-11 22:45:01 +02:00
}
2014-05-06 23:43:29 +02:00
2014-10-05 16:21:27 +02:00
void Battlenet :: Session :: HandleListSubscribeRequest ( WoWRealm :: ListSubscribeRequest const & /*listSubscribeRequest*/ )
2014-05-06 23:43:29 +02:00
{
2014-10-10 23:48:01 +02:00
WoWRealm :: ListSubscribeResponse * listSubscribeResponse = new WoWRealm :: ListSubscribeResponse ();
2014-05-06 23:43:29 +02:00
2014-05-13 22:02:51 +02:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_BNET_CHARACTER_COUNTS );
stmt -> setUInt32 ( 0 , _gameAccountId );
if ( PreparedQueryResult countResult = LoginDatabase . Query ( stmt ))
{
do
{
Field * fields = countResult -> Fetch ();
uint32 build = fields [ 4 ]. GetUInt32 ();
2014-10-11 15:13:30 +02:00
listSubscribeResponse -> CharacterCounts . push_back ({ RealmId ( fields [ 2 ]. GetUInt8 (), fields [ 3 ]. GetUInt8 (), fields [ 1 ]. GetUInt32 (), ( _build != build ? build : 0 )), fields [ 0 ]. GetUInt8 () });
2014-05-13 22:02:51 +02:00
} while ( countResult -> NextRow ());
}
2014-10-11 15:13:30 +02:00
for ( RealmList :: RealmMap :: value_type const & i : sRealmList -> GetRealms ())
listSubscribeResponse -> RealmData . push_back ( BuildListUpdate ( & i . second ));
2014-05-06 23:43:29 +02:00
2014-10-10 23:48:01 +02:00
listSubscribeResponse -> RealmData . push_back ( new WoWRealm :: ListComplete ());
2014-05-06 23:43:29 +02:00
2014-10-10 23:48:01 +02:00
AsyncWrite ( listSubscribeResponse );
2014-10-11 15:13:30 +02:00
_subscribedToRealmListUpdates = true ;
2014-05-06 23:43:29 +02:00
}
2014-10-11 15:13:30 +02:00
void Battlenet :: Session :: HandleListUnsubscribe ( WoWRealm :: ListUnsubscribe const & /*listUnsubscribe*/ )
2014-04-30 20:16:08 +02:00
{
2014-10-11 15:13:30 +02:00
_subscribedToRealmListUpdates = false ;
2014-05-06 23:43:29 +02:00
}
2014-10-10 23:48:01 +02:00
void Battlenet :: Session :: HandleJoinRequestV2 ( WoWRealm :: JoinRequestV2 const & joinRequest )
2014-04-30 20:16:08 +02:00
{
2014-10-10 23:48:01 +02:00
WoWRealm :: JoinResponseV2 * joinResponse = new WoWRealm :: JoinResponseV2 ();
Realm const * realm = sRealmList -> GetRealm ( joinRequest . Realm );
2014-11-01 11:40:17 +01:00
if ( ! realm || realm -> Flags & ( REALM_FLAG_INVALID | REALM_FLAG_OFFLINE ) || realm -> Id . Build != _build )
2014-05-06 23:43:29 +02:00
{
2014-10-10 23:48:01 +02:00
joinResponse -> Response = WoWRealm :: JoinResponseV2 :: FAILURE ;
AsyncWrite ( joinResponse );
2014-10-05 16:21:27 +02:00
return ;
2014-05-06 23:43:29 +02:00
}
2014-11-01 11:40:17 +01:00
joinResponse -> ServerSeed = rand32 ();
2014-05-06 23:43:29 +02:00
uint8 sessionKey [ 40 ];
2014-06-03 18:15:35 +02:00
HmacSha1 hmac ( K . GetNumBytes (), K . AsByteArray (). get ());
2014-05-06 23:43:29 +02:00
hmac . UpdateData (( uint8 * ) "WoW \0 " , 4 );
2014-10-10 23:48:01 +02:00
hmac . UpdateData (( uint8 * ) & joinRequest . ClientSeed , 4 );
hmac . UpdateData (( uint8 * ) & joinResponse -> ServerSeed , 4 );
2014-05-06 23:43:29 +02:00
hmac . Finalize ();
memcpy ( sessionKey , hmac . GetDigest (), hmac . GetLength ());
2014-06-03 18:15:35 +02:00
HmacSha1 hmac2 ( K . GetNumBytes (), K . AsByteArray (). get ());
2014-05-06 23:43:29 +02:00
hmac2 . UpdateData (( uint8 * ) "WoW \0 " , 4 );
2014-10-10 23:48:01 +02:00
hmac2 . UpdateData (( uint8 * ) & joinResponse -> ServerSeed , 4 );
hmac2 . UpdateData (( uint8 * ) & joinRequest . ClientSeed , 4 );
2014-05-06 23:43:29 +02:00
hmac2 . Finalize ();
memcpy ( sessionKey + hmac . GetLength (), hmac2 . GetDigest (), hmac2 . GetLength ());
LoginDatabase . DirectPExecute ( "UPDATE account SET sessionkey = '%s', last_ip = '%s', last_login = NOW(), locale = %u, failed_logins = 0, os = '%s' WHERE id = %u" ,
2014-07-27 21:50:01 +02:00
ByteArrayToHexStr ( sessionKey , 40 , true ). c_str (), GetRemoteIpAddress (). to_string (). c_str (), GetLocaleByName ( _locale ), _os . c_str (), _gameAccountId );
2014-05-06 23:43:29 +02:00
2014-10-11 15:13:30 +02:00
joinResponse -> IPv4 . emplace_back ( realm -> ExternalAddress , realm -> Port );
2014-05-06 23:43:29 +02:00
if ( realm -> ExternalAddress != realm -> LocalAddress )
2014-10-11 15:13:30 +02:00
joinResponse -> IPv4 . emplace_back ( realm -> LocalAddress , realm -> Port );
2014-05-06 23:43:29 +02:00
2014-10-10 23:48:01 +02:00
AsyncWrite ( joinResponse );
2014-04-30 20:16:08 +02:00
}
2014-10-11 15:13:30 +02:00
void Battlenet :: Session :: HandleSocialNetworkCheckConnected ( Friends :: SocialNetworkCheckConnected const & socialNetworkCheckConnected )
{
Friends :: SocialNetworkCheckConnectedResult * socialNetworkCheckConnectedResult = new Friends :: SocialNetworkCheckConnectedResult ();
socialNetworkCheckConnectedResult -> SocialNetworkId = socialNetworkCheckConnected . SocialNetworkId ;
AsyncWrite ( socialNetworkCheckConnectedResult );
}
2014-10-13 17:22:24 +02:00
void Battlenet :: Session :: HandleGetStreamItemsRequest ( Cache :: GetStreamItemsRequest const & getStreamItemsRequest )
{
if ( ModuleInfo * module = sModuleMgr -> CreateModule ( getStreamItemsRequest . Locale , getStreamItemsRequest . ItemName ))
{
Cache :: GetStreamItemsResponse * getStreamItemsResponse = new Cache :: GetStreamItemsResponse ();
getStreamItemsResponse -> Index = getStreamItemsRequest . Index ;
getStreamItemsResponse -> Modules . push_back ( module );
AsyncWrite ( getStreamItemsResponse );
}
}
2014-10-12 20:58:25 +02:00
inline std :: string PacketToStringHelper ( Battlenet :: ClientPacket const * packet )
{
if ( sLog -> ShouldLog ( "session.packets" , LOG_LEVEL_TRACE ))
return packet -> ToString ();
return sPacketManager . GetClientPacketName ( packet -> GetHeader ());
}
inline std :: string PacketToStringHelper ( Battlenet :: ServerPacket const * packet )
{
if ( sLog -> ShouldLog ( "session.packets" , LOG_LEVEL_TRACE ))
return packet -> ToString ();
return sPacketManager . GetServerPacketName ( packet -> GetHeader ());
2014-04-30 20:16:08 +02:00
}
2014-09-12 22:09:07 +02:00
void Battlenet :: Session :: ReadHandler ()
2014-04-30 20:16:08 +02:00
{
2014-10-05 16:21:27 +02:00
BitStream stream ( std :: move ( GetReadBuffer ()));
_crypt . DecryptRecv ( stream . GetBuffer (), stream . GetSize ());
2014-06-01 13:56:36 +02:00
2014-10-05 16:21:27 +02:00
while ( ! stream . IsRead ())
2014-07-27 21:50:01 +02:00
{
try
2014-07-19 17:03:32 +02:00
{
2014-07-27 21:50:01 +02:00
PacketHeader header ;
2014-10-05 16:21:27 +02:00
header . Opcode = stream . Read < uint32 > ( 6 );
if ( stream . Read < bool > ( 1 ))
header . Channel = stream . Read < int32 > ( 4 );
2014-07-19 17:03:32 +02:00
2014-07-27 21:50:01 +02:00
if ( header . Channel != AUTHENTICATION && ! _authed )
{
2014-10-12 15:34:01 +02:00
TC_LOG_DEBUG ( "session.packets" , "%s Received not allowed %s. Client has not authed yet." , GetClientInfo (). c_str (), header . ToString (). c_str ());
2014-07-27 21:50:01 +02:00
CloseSocket ();
return ;
}
2014-07-19 17:03:32 +02:00
2014-10-12 20:58:25 +02:00
if ( ClientPacket * packet = sPacketManager . CreateClientPacket ( header , stream ))
2014-07-27 21:50:01 +02:00
{
2014-10-12 20:58:25 +02:00
if ( sPacketManager . IsHandled ( header ))
TC_LOG_DEBUG ( "session.packets" , "%s Received %s" , GetClientInfo (). c_str (), PacketToStringHelper ( packet ). c_str ());
2014-10-11 22:45:01 +02:00
2014-10-12 20:58:25 +02:00
packet -> CallHandler ( this );
2014-10-05 16:21:27 +02:00
delete packet ;
2014-04-30 20:16:08 +02:00
}
2014-10-12 20:58:25 +02:00
else if ( sPacketManager . GetClientPacketName ( header ))
{
LogUnhandledPacket ( header );
break ;
2014-04-30 20:16:08 +02:00
}
2014-07-27 21:50:01 +02:00
else
2014-04-30 20:16:08 +02:00
{
2014-10-12 15:34:01 +02:00
TC_LOG_DEBUG ( "session.packets" , "%s Received unknown %s" , GetClientInfo (). c_str (), header . ToString (). c_str ());
2014-07-27 21:50:01 +02:00
break ;
2014-04-30 20:16:08 +02:00
}
2014-07-27 21:50:01 +02:00
2014-10-05 16:21:27 +02:00
stream . AlignToNextByte ();
2014-07-27 21:50:01 +02:00
}
catch ( BitStreamPositionException const & e )
{
2014-10-12 15:34:01 +02:00
TC_LOG_ERROR ( "session.packets" , "%s Exception thrown during packet processing %s" , GetClientInfo (). c_str (), e . what ());
2014-07-27 21:50:01 +02:00
CloseSocket ();
return ;
2014-05-06 23:43:29 +02:00
}
2014-07-27 21:50:01 +02:00
}
2014-04-30 20:16:08 +02:00
2014-09-12 22:09:07 +02:00
GetReadBuffer (). Resize ( size_t ( BufferSizes :: Read ));
AsyncRead ();
2014-04-30 20:16:08 +02:00
}
2014-07-19 17:03:32 +02:00
void Battlenet :: Session :: Start ()
2014-04-30 20:16:08 +02:00
{
2014-10-12 15:34:01 +02:00
TC_LOG_TRACE ( "session" , "Accepted connection from %s" , GetRemoteIpAddress (). to_string (). c_str ());
2014-09-12 22:09:07 +02:00
AsyncRead ();
2014-04-30 20:16:08 +02:00
}
2014-04-30 23:02:01 +02:00
2014-07-19 17:03:32 +02:00
void Battlenet :: Session :: AsyncWrite ( ServerPacket * packet )
2014-04-30 23:02:01 +02:00
{
2014-08-12 00:44:26 +02:00
if ( ! IsOpen ())
{
delete packet ;
return ;
}
2014-10-12 20:58:25 +02:00
TC_LOG_DEBUG ( "session.packets" , "%s Sending %s" , GetClientInfo (). c_str (), PacketToStringHelper ( packet ). c_str ());
2014-07-19 17:03:32 +02:00
packet -> Write ();
2014-05-10 12:50:27 +02:00
2014-09-12 22:09:07 +02:00
MessageBuffer buffer ;
buffer . Write ( packet -> GetData (), packet -> GetSize ());
delete packet ;
2014-07-19 17:03:32 +02:00
2014-09-12 22:09:07 +02:00
std :: unique_lock < std :: mutex > guard ( _writeLock );
2014-07-19 17:03:32 +02:00
2014-09-12 22:09:07 +02:00
_crypt . EncryptSend ( buffer . GetReadPointer (), buffer . GetActiveSize ());
2014-07-27 21:50:01 +02:00
2014-09-12 22:09:07 +02:00
QueuePacket ( std :: move ( buffer ), guard );
2014-04-30 23:02:01 +02:00
}
2014-05-06 23:43:29 +02:00
inline void ReplaceResponse ( Battlenet :: ServerPacket ** oldResponse , Battlenet :: ServerPacket * newResponse )
{
if ( * oldResponse )
delete * oldResponse ;
* oldResponse = newResponse ;
}
2014-07-19 17:03:32 +02:00
bool Battlenet :: Session :: HandlePasswordModule ( BitStream * dataStream , ServerPacket ** response )
2014-05-06 23:43:29 +02:00
{
if ( dataStream -> GetSize () != 1 + 128 + 32 + 128 )
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( AUTH_CORRUPTED_MODULE );
ReplaceResponse ( response , logonResponse );
2014-05-06 23:43:29 +02:00
return false ;
}
if ( dataStream -> Read < uint8 > ( 8 ) != 2 ) // State
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( AUTH_CORRUPTED_MODULE );
ReplaceResponse ( response , logonResponse );
2014-05-06 23:43:29 +02:00
return false ;
}
BigNumber A , clientM1 , clientChallenge ;
A . SetBinary ( dataStream -> ReadBytes ( 128 ). get (), 128 );
clientM1 . SetBinary ( dataStream -> ReadBytes ( 32 ). get (), 32 );
clientChallenge . SetBinary ( dataStream -> ReadBytes ( 128 ). get (), 128 );
2014-11-06 18:49:19 +01:00
if ( A . IsZero ())
2014-05-06 23:43:29 +02:00
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( AUTH_CORRUPTED_MODULE );
ReplaceResponse ( response , logonResponse );
2014-05-06 23:43:29 +02:00
return false ;
}
SHA256Hash sha ;
sha . UpdateBigNumbers ( & A , & B , NULL );
sha . Finalize ();
BigNumber u ;
u . SetBinary ( sha . GetDigest (), sha . GetLength ());
BigNumber S = (( A * v . ModExp ( u , N )) % N ). ModExp ( b , N );
uint8 S_bytes [ 128 ];
memcpy ( S_bytes , S . AsByteArray ( 128 ). get (), 128 );
uint8 part1 [ 64 ];
uint8 part2 [ 64 ];
for ( int i = 0 ; i < 64 ; ++ i )
{
part1 [ i ] = S_bytes [ i * 2 ];
part2 [ i ] = S_bytes [ i * 2 + 1 ];
}
SHA256Hash part1sha , part2sha ;
part1sha . UpdateData ( part1 , 64 );
part1sha . Finalize ();
part2sha . UpdateData ( part2 , 64 );
part2sha . Finalize ();
uint8 sessionKey [ SHA256_DIGEST_LENGTH * 2 ];
for ( int i = 0 ; i < SHA256_DIGEST_LENGTH ; ++ i )
{
sessionKey [ i * 2 ] = part1sha . GetDigest ()[ i ];
sessionKey [ i * 2 + 1 ] = part2sha . GetDigest ()[ i ];
}
K . SetBinary ( sessionKey , SHA256_DIGEST_LENGTH * 2 );
BigNumber M1 ;
uint8 hash [ SHA256_DIGEST_LENGTH ];
sha . Initialize ();
sha . UpdateBigNumbers ( & N , NULL );
sha . Finalize ();
memcpy ( hash , sha . GetDigest (), sha . GetLength ());
sha . Initialize ();
sha . UpdateBigNumbers ( & g , NULL );
sha . Finalize ();
for ( int i = 0 ; i < sha . GetLength (); ++ i )
hash [ i ] ^= sha . GetDigest ()[ i ];
SHA256Hash shaI ;
shaI . UpdateData ( ByteArrayToHexStr ( I . AsByteArray (). get (), 32 ));
shaI . Finalize ();
// Concat all variables for M1 hash
sha . Initialize ();
sha . UpdateData ( hash , SHA256_DIGEST_LENGTH );
sha . UpdateData ( shaI . GetDigest (), shaI . GetLength ());
sha . UpdateBigNumbers ( & s , & A , & B , & K , NULL );
sha . Finalize ();
M1 . SetBinary ( sha . GetDigest (), sha . GetLength ());
if ( memcmp ( M1 . AsByteArray (). get (), clientM1 . AsByteArray (). get (), 32 ))
{
2014-10-04 15:45:33 +02:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_BNET_FAILED_LOGINS );
stmt -> setString ( 0 , _accountName );
LoginDatabase . Execute ( stmt );
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( AUTH_UNKNOWN_ACCOUNT );
ReplaceResponse ( response , logonResponse );
2014-11-09 19:18:54 +01:00
TC_LOG_DEBUG ( "session" , "[Battlenet::Password] %s attempted to log in with invalid password!" , GetClientInfo (). c_str ());
2014-05-06 23:43:29 +02:00
return false ;
}
uint64 numAccounts = 0 ;
2014-05-13 22:02:51 +02:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_BNET_GAME_ACCOUNTS );
stmt -> setUInt32 ( 0 , _accountId );
PreparedQueryResult result = LoginDatabase . Query ( stmt );
2014-05-06 23:43:29 +02:00
if ( result )
numAccounts = result -> GetRowCount ();
if ( ! numAccounts )
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( LOGIN_NO_GAME_ACCOUNT );
ReplaceResponse ( response , logonResponse );
2014-11-09 19:18:54 +01:00
TC_LOG_DEBUG ( "session" , "[Battlenet::Password] %s does not have any linked game accounts!" , GetClientInfo (). c_str ());
2014-05-06 23:43:29 +02:00
return false ;
}
Field * fields = result -> Fetch ();
//set expired game account bans to inactive
LoginDatabase . DirectExecute ( LoginDatabase . GetPreparedStatement ( LOGIN_UPD_EXPIRED_ACCOUNT_BANS ));
BigNumber M ;
sha . Initialize ();
sha . UpdateBigNumbers ( & A , & M1 , & K , NULL );
sha . Finalize ();
M . SetBinary ( sha . GetDigest (), sha . GetLength ());
2014-06-03 19:15:12 +02:00
BigNumber serverProof ;
serverProof . SetRand ( 128 * 8 ); // just send garbage, server signature check is patched out in client
2014-05-06 23:43:29 +02:00
BitStream stream ;
2014-10-10 23:19:40 +02:00
ModuleInfo * password = sModuleMgr -> CreateModule ( _os , "Password" );
2014-05-06 23:43:29 +02:00
uint8 state = 3 ;
stream . WriteBytes ( & state , 1 );
stream . WriteBytes ( M . AsByteArray ( 32 ). get (), 32 );
2014-06-03 19:15:12 +02:00
stream . WriteBytes ( serverProof . AsByteArray ( 128 ). get (), 128 );
2014-05-06 23:43:29 +02:00
password -> DataSize = stream . GetSize ();
password -> Data = new uint8 [ password -> DataSize ];
memcpy ( password -> Data , stream . GetBuffer (), password -> DataSize );
2014-10-10 23:48:01 +02:00
Authentication :: ProofRequest * proofRequest = new Authentication :: ProofRequest ();
proofRequest -> Modules . push_back ( password );
2014-05-06 23:43:29 +02:00
if ( numAccounts > 1 )
{
BitStream accounts ;
state = 0 ;
accounts . WriteBytes ( & state , 1 );
accounts . Write ( numAccounts , 8 );
do
{
fields = result -> Fetch ();
2014-06-08 14:54:37 +02:00
std :: ostringstream name ;
2014-08-22 20:09:18 +02:00
std :: string originalName = fields [ 1 ]. GetString ();
2014-11-10 01:19:24 +01:00
std :: size_t hashPos = originalName . find ( '#' );
if ( hashPos != std :: string :: npos )
name << "WoW" << originalName . substr ( hashPos + 1 );
2014-08-22 20:09:18 +02:00
else
name << originalName ;
2014-05-06 23:43:29 +02:00
accounts . Write ( 2 , 8 );
2014-06-08 14:54:37 +02:00
accounts . WriteString ( name . str (), 8 );
2014-05-06 23:43:29 +02:00
} while ( result -> NextRow ());
2014-10-10 23:19:40 +02:00
ModuleInfo * selectGameAccount = sModuleMgr -> CreateModule ( _os , "SelectGameAccount" );
2014-05-06 23:43:29 +02:00
selectGameAccount -> DataSize = accounts . GetSize ();
selectGameAccount -> Data = new uint8 [ selectGameAccount -> DataSize ];
memcpy ( selectGameAccount -> Data , accounts . GetBuffer (), selectGameAccount -> DataSize );
2014-10-10 23:48:01 +02:00
proofRequest -> Modules . push_back ( selectGameAccount );
2014-05-06 23:43:29 +02:00
_modulesWaitingForData . push ( MODULE_SELECT_GAME_ACCOUNT );
}
else
{
if ( fields [ 4 ]. GetBool ())
{
2014-10-10 23:48:01 +02:00
delete proofRequest ;
2014-05-06 23:43:29 +02:00
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
2014-05-06 23:43:29 +02:00
if ( fields [ 2 ]. GetUInt32 () == fields [ 3 ]. GetUInt32 ())
{
2014-10-10 23:48:01 +02:00
logonResponse -> SetAuthResult ( LOGIN_BANNED );
2014-10-12 15:34:01 +02:00
TC_LOG_DEBUG ( "session" , "'%s:%d' [Battlenet::Password] Banned account %s tried to login!" , GetRemoteIpAddress (). to_string (). c_str (), GetRemotePort (), _accountName . c_str ());
2014-05-06 23:43:29 +02:00
}
else
{
2014-10-10 23:48:01 +02:00
logonResponse -> SetAuthResult ( LOGIN_SUSPENDED );
2014-10-12 15:34:01 +02:00
TC_LOG_DEBUG ( "session" , "'%s:%d' [Battlenet::Password] Temporarily banned account %s tried to login!" , GetRemoteIpAddress (). to_string (). c_str (), GetRemotePort (), _accountName . c_str ());
2014-05-06 23:43:29 +02:00
}
2014-10-10 23:48:01 +02:00
ReplaceResponse ( response , logonResponse );
2014-06-01 02:39:55 +02:00
return false ;
2014-05-06 23:43:29 +02:00
}
2014-08-22 20:09:18 +02:00
_gameAccountId = fields [ 0 ]. GetUInt32 ();
_gameAccountName = fields [ 1 ]. GetString ();
2014-05-06 23:43:29 +02:00
2014-10-10 23:48:01 +02:00
proofRequest -> Modules . push_back ( sModuleMgr -> CreateModule ( _os , "RiskFingerprint" ));
2014-05-06 23:43:29 +02:00
_modulesWaitingForData . push ( MODULE_RISK_FINGERPRINT );
}
2014-10-10 23:48:01 +02:00
ReplaceResponse ( response , proofRequest );
2014-05-06 23:43:29 +02:00
return true ;
}
2014-07-19 17:03:32 +02:00
bool Battlenet :: Session :: HandleSelectGameAccountModule ( BitStream * dataStream , ServerPacket ** response )
2014-05-06 23:43:29 +02:00
{
if ( dataStream -> Read < uint8 > ( 8 ) != 1 )
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( AUTH_CORRUPTED_MODULE );
ReplaceResponse ( response , logonResponse );
2014-05-06 23:43:29 +02:00
return false ;
}
dataStream -> Read < uint8 > ( 8 );
std :: string account = dataStream -> ReadString ( 8 );
2014-08-22 20:09:18 +02:00
if ( account . empty ())
2014-06-08 14:54:37 +02:00
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( LOGIN_NO_GAME_ACCOUNT );
ReplaceResponse ( response , logonResponse );
2014-06-08 14:54:37 +02:00
return false ;
}
2014-08-22 20:09:18 +02:00
PreparedStatement * stmt ;
if ( account . substr ( 0 , 3 ) != "WoW" )
{
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_BNET_GAME_ACCOUNT );
stmt -> setString ( 0 , account );
}
else
{
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_BNET_GAME_ACCOUNT_UNNAMED );
stmt -> setUInt8 ( 0 , atol ( account . substr ( 3 ). c_str ()));
}
2014-05-06 23:43:29 +02:00
2014-05-13 22:02:51 +02:00
stmt -> setUInt32 ( 1 , _accountId );
PreparedQueryResult result = LoginDatabase . Query ( stmt );
2014-05-06 23:43:29 +02:00
if ( ! result )
{
2014-10-04 15:45:33 +02:00
Authentication :: LogonResponse * complete = new Authentication :: LogonResponse ();
2014-05-06 23:43:29 +02:00
complete -> SetAuthResult ( LOGIN_NO_GAME_ACCOUNT );
ReplaceResponse ( response , complete );
2014-11-09 19:18:54 +01:00
TC_LOG_DEBUG ( "session" , "[Battlenet::SelectGameAccount] %s attempted to log in with invalid game account name %s!" , GetClientInfo (). c_str (), account . c_str ());
2014-05-06 23:43:29 +02:00
return false ;
}
Field * fields = result -> Fetch ();
2014-08-22 20:09:18 +02:00
if ( fields [ 4 ]. GetBool ())
2014-05-06 23:43:29 +02:00
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
2014-08-22 20:09:18 +02:00
if ( fields [ 2 ]. GetUInt32 () == fields [ 3 ]. GetUInt32 ())
2014-05-06 23:43:29 +02:00
{
2014-10-10 23:48:01 +02:00
logonResponse -> SetAuthResult ( LOGIN_BANNED );
2014-10-12 15:34:01 +02:00
TC_LOG_DEBUG ( "session" , "'%s:%d' [Battlenet::SelectGameAccount] Banned account %s tried to login!" , GetRemoteIpAddress (). to_string (). c_str (), GetRemotePort (), _accountName . c_str ());
2014-05-06 23:43:29 +02:00
}
else
{
2014-10-10 23:48:01 +02:00
logonResponse -> SetAuthResult ( LOGIN_SUSPENDED );
2014-10-12 15:34:01 +02:00
TC_LOG_DEBUG ( "session" , "'%s:%d' [Battlenet::SelectGameAccount] Temporarily banned account %s tried to login!" , GetRemoteIpAddress (). to_string (). c_str (), GetRemotePort (), _accountName . c_str ());
2014-05-06 23:43:29 +02:00
}
2014-10-10 23:48:01 +02:00
ReplaceResponse ( response , logonResponse );
2014-06-01 02:39:55 +02:00
return false ;
2014-05-06 23:43:29 +02:00
}
_gameAccountId = fields [ 0 ]. GetUInt32 ();
2014-08-22 20:09:18 +02:00
_gameAccountName = fields [ 1 ]. GetString ();
2014-05-06 23:43:29 +02:00
2014-10-10 23:48:01 +02:00
Authentication :: ProofRequest * proofRequest = new Authentication :: ProofRequest ();
proofRequest -> Modules . push_back ( sModuleMgr -> CreateModule ( _os , "RiskFingerprint" ));
ReplaceResponse ( response , proofRequest );
2014-05-06 23:43:29 +02:00
_modulesWaitingForData . push ( MODULE_RISK_FINGERPRINT );
return true ;
}
2014-07-19 17:03:32 +02:00
bool Battlenet :: Session :: HandleRiskFingerprintModule ( BitStream * dataStream , ServerPacket ** response )
2014-05-06 23:43:29 +02:00
{
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
2014-05-06 23:43:29 +02:00
if ( dataStream -> Read < uint8 > ( 8 ) == 1 )
{
2014-10-10 23:48:01 +02:00
logonResponse -> AccountId = _accountId ;
logonResponse -> GameAccountName = _gameAccountName ;
logonResponse -> GameAccountFlags = GAMEACCOUNT_FLAG_PROPASS_LOCK ;
2014-10-04 15:45:33 +02:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_BNET_FAILED_LOGINS );
stmt -> setUInt32 ( 0 , _accountId );
if ( PreparedQueryResult failedLoginsResult = LoginDatabase . Query ( stmt ))
2014-10-10 23:48:01 +02:00
logonResponse -> FailedLogins = ( * failedLoginsResult )[ 0 ]. GetUInt32 ();
2014-06-01 13:56:36 +02:00
SQLTransaction trans = LoginDatabase . BeginTransaction ();
2014-05-06 23:43:29 +02:00
2014-10-04 15:45:33 +02:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_BNET_LAST_LOGIN_INFO );
2014-07-27 21:50:01 +02:00
stmt -> setString ( 0 , GetRemoteIpAddress (). to_string ());
2014-05-13 22:02:51 +02:00
stmt -> setUInt8 ( 1 , GetLocaleByName ( _locale ));
stmt -> setString ( 2 , _os );
stmt -> setUInt32 ( 3 , _accountId );
2014-06-01 13:56:36 +02:00
trans -> Append ( stmt );
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_BNET_SESSION_KEY );
stmt -> setString ( 0 , K . AsHexStr ());
2014-06-08 17:58:28 +02:00
stmt -> setBool ( 1 , true );
stmt -> setUInt32 ( 2 , _accountId );
2014-06-01 13:56:36 +02:00
trans -> Append ( stmt );
LoginDatabase . CommitTransaction ( trans );
_authed = true ;
2014-10-10 23:19:40 +02:00
sSessionMgr . AddSession ( this );
2014-05-06 23:43:29 +02:00
}
else
2014-10-10 23:48:01 +02:00
logonResponse -> SetAuthResult ( AUTH_BAD_VERSION_HASH );
2014-05-06 23:43:29 +02:00
2014-10-10 23:48:01 +02:00
ReplaceResponse ( response , logonResponse );
2014-06-01 13:03:30 +02:00
return true ;
2014-05-06 23:43:29 +02:00
}
2014-07-19 17:03:32 +02:00
bool Battlenet :: Session :: HandleResumeModule ( BitStream * dataStream , ServerPacket ** response )
2014-06-01 13:56:36 +02:00
{
if ( dataStream -> Read < uint8 > ( 8 ) != 1 )
{
2014-10-10 23:48:01 +02:00
Authentication :: ResumeResponse * resumeResponse = new Authentication :: ResumeResponse ();
resumeResponse -> SetAuthResult ( AUTH_CORRUPTED_MODULE );
ReplaceResponse ( response , resumeResponse );
2014-06-01 13:56:36 +02:00
return false ;
}
static uint8 const ResumeClient = 0 ;
static uint8 const ResumeServer = 1 ;
2014-07-19 17:03:32 +02:00
std :: unique_ptr < uint8 [] > clientChallenge = dataStream -> ReadBytes ( 16 );
std :: unique_ptr < uint8 [] > clientProof = dataStream -> ReadBytes ( 32 );
2014-08-22 20:09:18 +02:00
std :: unique_ptr < uint8 [] > serverChallenge = _reconnectProof . AsByteArray ( 16 );
std :: unique_ptr < uint8 [] > sessionKey = K . AsByteArray ( 64 );
2014-06-01 13:56:36 +02:00
2014-06-03 18:15:35 +02:00
HmacSha256 clientPart ( 64 , sessionKey . get ());
2014-06-01 13:56:36 +02:00
clientPart . UpdateData ( & ResumeClient , 1 );
clientPart . UpdateData ( clientChallenge . get (), 16 );
clientPart . UpdateData ( serverChallenge . get (), 16 );
clientPart . Finalize ();
2014-06-03 18:15:35 +02:00
HmacSha256 serverPart ( 64 , sessionKey . get ());
2014-06-01 13:56:36 +02:00
serverPart . UpdateData ( & ResumeServer , 1 );
serverPart . UpdateData ( serverChallenge . get (), 16 );
serverPart . UpdateData ( clientChallenge . get (), 16 );
serverPart . Finalize ();
uint8 newSessionKey [ 64 ];
memcpy ( & newSessionKey [ 0 ], clientPart . GetDigest (), clientPart . GetLength ());
memcpy ( & newSessionKey [ 32 ], serverPart . GetDigest (), serverPart . GetLength ());
K . SetBinary ( newSessionKey , 64 );
2014-06-03 18:15:35 +02:00
HmacSha256 proof ( 64 , newSessionKey );
2014-06-01 13:56:36 +02:00
proof . UpdateData ( & ResumeClient , 1 );
proof . UpdateData ( clientChallenge . get (), 16 );
proof . UpdateData ( serverChallenge . get (), 16 );
proof . Finalize ();
if ( memcmp ( proof . GetDigest (), clientProof . get (), serverPart . GetLength ()))
{
2014-10-04 15:45:33 +02:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_BNET_FAILED_LOGINS );
stmt -> setString ( 0 , _accountName );
LoginDatabase . Execute ( stmt );
2014-11-09 19:18:54 +01:00
TC_LOG_DEBUG ( "session" , "[Battlenet::Resume] %s attempted to reconnect with invalid password!" , GetClientInfo (). c_str ());
2014-10-10 23:48:01 +02:00
Authentication :: ResumeResponse * resumeResponse = new Authentication :: ResumeResponse ();
resumeResponse -> SetAuthResult ( AUTH_UNKNOWN_ACCOUNT );
ReplaceResponse ( response , resumeResponse );
2014-06-01 13:56:36 +02:00
return false ;
}
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_BNET_SESSION_KEY );
stmt -> setString ( 0 , K . AsHexStr ());
2014-06-08 17:58:28 +02:00
stmt -> setBool ( 1 , true );
stmt -> setUInt32 ( 2 , _accountId );
2014-06-01 13:56:36 +02:00
LoginDatabase . Execute ( stmt );
2014-06-03 18:15:35 +02:00
HmacSha256 serverProof ( 64 , newSessionKey );
2014-06-01 13:56:36 +02:00
serverProof . UpdateData ( & ResumeServer , 1 );
serverProof . UpdateData ( serverChallenge . get (), 16 );
serverProof . UpdateData ( clientChallenge . get (), 16 );
serverProof . Finalize ();
2014-10-10 23:19:40 +02:00
ModuleInfo * resume = sModuleMgr -> CreateModule ( _os , "Resume" );
2014-06-01 13:56:36 +02:00
BitStream resumeData ;
uint8 state = 2 ;
resumeData . WriteBytes ( & state , 1 );
resumeData . WriteBytes ( serverProof . GetDigest (), serverProof . GetLength ());
resume -> DataSize = resumeData . GetSize ();
resume -> Data = new uint8 [ resume -> DataSize ];
memcpy ( resume -> Data , resumeData . GetBuffer (), resume -> DataSize );
2014-10-10 23:48:01 +02:00
Authentication :: ResumeResponse * resumeResponse = new Authentication :: ResumeResponse ();
resumeResponse -> Modules . push_back ( resume );
ReplaceResponse ( response , resumeResponse );
2014-06-01 13:56:36 +02:00
_authed = true ;
2014-10-10 23:19:40 +02:00
sSessionMgr . AddSession ( this );
2014-06-01 13:56:36 +02:00
return true ;
}
2014-07-19 17:03:32 +02:00
bool Battlenet :: Session :: UnhandledModule ( BitStream * /*dataStream*/ , ServerPacket ** response )
2014-05-06 23:43:29 +02:00
{
2014-10-12 15:34:01 +02:00
TC_LOG_ERROR ( "session.packets" , "Unhandled module." );
2014-10-10 23:48:01 +02:00
Authentication :: LogonResponse * logonResponse = new Authentication :: LogonResponse ();
logonResponse -> SetAuthResult ( AUTH_CORRUPTED_MODULE );
ReplaceResponse ( response , logonResponse );
2014-05-06 23:43:29 +02:00
return false ;
}
2014-10-11 15:13:30 +02:00
void Battlenet :: Session :: UpdateRealms ( std :: vector < Realm const *>& realms , std :: vector < RealmId >& deletedRealms )
{
for ( Realm const * realm : realms )
AsyncWrite ( BuildListUpdate ( realm ));
for ( RealmId & deleted : deletedRealms )
{
WoWRealm :: ListUpdate * listUpdate = new WoWRealm :: ListUpdate ();
listUpdate -> UpdateState = WoWRealm :: ListUpdate :: DELETED ;
listUpdate -> Id = deleted ;
AsyncWrite ( listUpdate );
}
}
Battlenet :: WoWRealm :: ListUpdate * Battlenet :: Session :: BuildListUpdate ( Realm const * realm ) const
{
uint32 flag = realm -> Flags & ~ REALM_FLAG_SPECIFYBUILD ;
RealmBuildInfo const * buildInfo = AuthHelper :: GetBuildInfo ( realm -> Id . Build );
if ( realm -> Id . Build != _build )
{
flag |= REALM_FLAG_INVALID ;
if ( buildInfo )
flag |= REALM_FLAG_SPECIFYBUILD ; // tell the client what build the realm is for
}
WoWRealm :: ListUpdate * listUpdate = new WoWRealm :: ListUpdate ();
listUpdate -> Timezone = realm -> Timezone ;
listUpdate -> Population = realm -> PopulationLevel ;
listUpdate -> Lock = ( realm -> AllowedSecurityLevel > _accountSecurityLevel ) ? 1 : 0 ;
listUpdate -> Type = realm -> Type ;
listUpdate -> Name = realm -> Name ;
if ( flag & REALM_FLAG_SPECIFYBUILD )
{
std :: ostringstream version ;
version << buildInfo -> MajorVersion << '.' << buildInfo -> MinorVersion << '.' << buildInfo -> BugfixVersion << '.' << buildInfo -> Build ;
listUpdate -> Version = version . str ();
listUpdate -> Address = realm -> GetAddressForClient ( GetRemoteIpAddress ());
}
listUpdate -> Flags = flag ;
listUpdate -> Id = realm -> Id ;
return listUpdate ;
}
2014-10-12 15:34:01 +02:00
std :: string Battlenet :: Session :: GetClientInfo () const
{
std :: ostringstream stream ;
stream << '[' << GetRemoteIpAddress () << ':' << GetRemotePort ();
if ( ! _accountName . empty ())
stream << ", Account: " << _accountName ;
if ( ! _gameAccountName . empty ())
stream << ", Game account: " << _gameAccountName ;
stream << ']' ;
return stream . str ();
}