2014-04-13 07:04:56 -04:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
# include "Authenticator.h"
2014-07-29 11:45:55 -04:00
# include "MojangAPI.h"
2014-04-14 16:52:59 -04:00
# include "../Root.h"
# include "../Server.h"
# include "../ClientHandle.h"
2014-04-13 07:04:56 -04:00
2014-10-23 09:15:10 -04:00
# include "../IniFile.h"
2014-04-13 07:04:56 -04:00
# include "json/json.h"
2014-04-27 16:28:14 -04:00
# include "PolarSSL++/BlockingSslClientSocket.h"
2014-04-13 07:04:56 -04:00
# define DEFAULT_AUTH_SERVER "sessionserver.mojang.com"
# define DEFAULT_AUTH_ADDRESS " / session / minecraft / hasJoined?username=%USERNAME%&serverId=%SERVERID%"
2015-05-21 04:26:45 -04:00
2014-04-13 07:04:56 -04:00
cAuthenticator : : cAuthenticator ( void ) :
2014-04-14 16:52:59 -04:00
super ( " cAuthenticator " ) ,
m_Server ( DEFAULT_AUTH_SERVER ) ,
m_Address ( DEFAULT_AUTH_ADDRESS ) ,
m_ShouldAuthenticate ( true )
2014-04-13 07:04:56 -04:00
{
}
cAuthenticator : : ~ cAuthenticator ( )
{
Stop ( ) ;
}
2015-05-14 10:47:51 -04:00
void cAuthenticator : : ReadSettings ( cSettingsRepositoryInterface & a_Settings )
2014-04-13 07:04:56 -04:00
{
2015-05-14 10:47:51 -04:00
m_Server = a_Settings . GetValueSet ( " Authentication " , " Server " , DEFAULT_AUTH_SERVER ) ;
m_Address = a_Settings . GetValueSet ( " Authentication " , " Address " , DEFAULT_AUTH_ADDRESS ) ;
m_ShouldAuthenticate = a_Settings . GetValueSetB ( " Authentication " , " Authenticate " , true ) ;
2014-04-13 07:04:56 -04:00
}
void cAuthenticator : : Authenticate ( int a_ClientID , const AString & a_UserName , const AString & a_ServerHash )
{
if ( ! m_ShouldAuthenticate )
{
2014-07-15 19:03:47 -04:00
Json : : Value Value ;
cRoot : : Get ( ) - > AuthenticateUser ( a_ClientID , a_UserName , cClientHandle : : GenerateOfflineUUID ( a_UserName ) , Value ) ;
2014-04-13 07:04:56 -04:00
return ;
}
cCSLock LOCK ( m_CS ) ;
m_Queue . push_back ( cUser ( a_ClientID , a_UserName , a_ServerHash ) ) ;
m_QueueNonempty . Set ( ) ;
}
2015-05-14 10:47:51 -04:00
void cAuthenticator : : Start ( cSettingsRepositoryInterface & a_Settings )
2014-04-13 07:04:56 -04:00
{
2015-05-14 10:47:51 -04:00
ReadSettings ( a_Settings ) ;
2014-04-13 07:04:56 -04:00
m_ShouldTerminate = false ;
super : : Start ( ) ;
}
void cAuthenticator : : Stop ( void )
{
m_ShouldTerminate = true ;
m_QueueNonempty . Set ( ) ;
Wait ( ) ;
}
void cAuthenticator : : Execute ( void )
{
for ( ; ; )
{
cCSLock Lock ( m_CS ) ;
while ( ! m_ShouldTerminate & & ( m_Queue . size ( ) = = 0 ) )
{
cCSUnlock Unlock ( Lock ) ;
m_QueueNonempty . Wait ( ) ;
}
if ( m_ShouldTerminate )
{
return ;
}
ASSERT ( ! m_Queue . empty ( ) ) ;
2014-04-18 15:44:58 -04:00
cAuthenticator : : cUser & User = m_Queue . front ( ) ;
int ClientID = User . m_ClientID ;
AString UserName = User . m_Name ;
AString ServerID = User . m_ServerID ;
2014-04-13 07:04:56 -04:00
m_Queue . pop_front ( ) ;
Lock . Unlock ( ) ;
AString NewUserName = UserName ;
AString UUID ;
2014-07-16 06:21:02 -04:00
Json : : Value Properties ;
if ( AuthWithYggdrasil ( NewUserName , ServerID , UUID , Properties ) )
2014-04-13 07:04:56 -04:00
{
2014-07-16 06:21:02 -04:00
LOGINFO ( " User %s authenticated with UUID %s " , NewUserName . c_str ( ) , UUID . c_str ( ) ) ;
2014-07-14 14:49:31 -04:00
cRoot : : Get ( ) - > AuthenticateUser ( ClientID , NewUserName , UUID , Properties ) ;
2014-04-13 07:04:56 -04:00
}
else
{
cRoot : : Get ( ) - > KickUser ( ClientID , " Failed to authenticate account! " ) ;
}
} // for (-ever)
}
2014-07-16 06:21:02 -04:00
bool cAuthenticator : : AuthWithYggdrasil ( AString & a_UserName , const AString & a_ServerId , AString & a_UUID , Json : : Value & a_Properties )
2014-07-14 14:49:31 -04:00
{
2014-07-17 16:50:58 -04:00
LOGD ( " Trying to authenticate user %s " , a_UserName . c_str ( ) ) ;
2014-07-14 14:49:31 -04:00
// Create the GET request:
AString ActualAddress = m_Address ;
ReplaceString ( ActualAddress , " %USERNAME% " , a_UserName ) ;
ReplaceString ( ActualAddress , " %SERVERID% " , a_ServerId ) ;
AString Request ;
Request + = " GET " + ActualAddress + " HTTP/1.0 \r \n " ;
Request + = " Host: " + m_Server + " \r \n " ;
Request + = " User-Agent: MCServer \r \n " ;
Request + = " Connection: close \r \n " ;
Request + = " \r \n " ;
AString Response ;
2014-07-29 11:45:55 -04:00
if ( ! cMojangAPI : : SecureRequest ( m_Server , Request , Response ) )
2014-07-14 14:49:31 -04:00
{
return false ;
}
2014-04-13 07:04:56 -04:00
2014-04-14 16:52:59 -04:00
// Check the HTTP status line:
2014-07-14 14:49:31 -04:00
const AString Prefix ( " HTTP/1.1 200 OK " ) ;
2014-04-14 16:52:59 -04:00
AString HexDump ;
2014-07-14 14:49:31 -04:00
if ( Response . compare ( 0 , Prefix . size ( ) , Prefix ) )
2014-04-14 16:52:59 -04:00
{
2014-07-14 14:49:31 -04:00
LOGINFO ( " User %s failed to auth, bad HTTP status line received " , a_UserName . c_str ( ) ) ;
LOGD ( " Response: \n %s " , CreateHexDump ( HexDump , Response . data ( ) , Response . size ( ) , 16 ) . c_str ( ) ) ;
2014-04-13 07:04:56 -04:00
return false ;
2014-04-14 16:52:59 -04:00
}
2014-04-13 07:04:56 -04:00
2014-04-14 16:52:59 -04:00
// Erase the HTTP headers from the response:
size_t idxHeadersEnd = Response . find ( " \r \n \r \n " ) ;
if ( idxHeadersEnd = = AString : : npos )
2014-04-13 07:04:56 -04:00
{
2014-07-14 14:49:31 -04:00
LOGINFO ( " User %s failed to authenticate, bad HTTP response header received " , a_UserName . c_str ( ) ) ;
LOGD ( " Response: \n %s " , CreateHexDump ( HexDump , Response . data ( ) , Response . size ( ) , 16 ) . c_str ( ) ) ;
2014-04-14 16:52:59 -04:00
return false ;
2014-04-13 07:04:56 -04:00
}
2014-04-14 16:52:59 -04:00
Response . erase ( 0 , idxHeadersEnd + 4 ) ;
2014-04-13 07:04:56 -04:00
2014-04-14 16:52:59 -04:00
// Parse the Json response:
if ( Response . empty ( ) )
{
2014-04-13 07:04:56 -04:00
return false ;
2014-04-14 16:52:59 -04:00
}
2014-04-13 07:04:56 -04:00
Json : : Value root ;
Json : : Reader reader ;
2014-04-14 16:52:59 -04:00
if ( ! reader . parse ( Response , root , false ) )
2014-04-13 07:04:56 -04:00
{
2014-07-14 14:49:31 -04:00
LOGWARNING ( " cAuthenticator: Cannot parse received data (authentication) to JSON! " ) ;
2014-04-13 07:04:56 -04:00
return false ;
}
a_UserName = root . get ( " name " , " Unknown " ) . asString ( ) ;
2014-07-30 07:52:51 -04:00
a_UUID = cMojangAPI : : MakeUUIDShort ( root . get ( " id " , " " ) . asString ( ) ) ;
2014-07-16 06:21:02 -04:00
a_Properties = root [ " properties " ] ;
2014-07-29 11:45:55 -04:00
2014-08-03 15:32:20 -04:00
// Store the player's profile in the MojangAPI caches:
cRoot : : Get ( ) - > GetMojangAPI ( ) . AddPlayerProfile ( a_UserName , a_UUID , a_Properties ) ;
2014-07-16 06:21:02 -04:00
2014-04-13 07:04:56 -04:00
return true ;
}
2014-07-16 06:21:02 -04:00
/* In case we want to export this function to the plugin API later - don't forget to add the relevant INI configuration lines for DEFAULT_PROPERTIES_ADDRESS
# define DEFAULT_PROPERTIES_ADDRESS " / session / minecraft / profile / %UUID%"
// Gets the properties, such as skin, of a player based on their UUID via Mojang's API
bool GetPlayerProperties ( const AString & a_UUID , Json : : Value & a_Properties ) ;
2014-07-15 19:03:47 -04:00
bool cAuthenticator : : GetPlayerProperties ( const AString & a_UUID , Json : : Value & a_Properties )
2014-07-14 14:49:31 -04:00
{
LOGD ( " Trying to get properties for user %s " , a_UUID . c_str ( ) ) ;
// Create the GET request:
AString ActualAddress = m_PropertiesAddress ;
ReplaceString ( ActualAddress , " %UUID% " , a_UUID ) ;
AString Request ;
Request + = " GET " + ActualAddress + " HTTP/1.0 \r \n " ;
Request + = " Host: " + m_Server + " \r \n " ;
Request + = " User-Agent: MCServer \r \n " ;
Request + = " Connection: close \r \n " ;
Request + = " \r \n " ;
AString Response ;
2014-07-19 00:38:55 -04:00
if ( ! ConnectSecurelyToAddress ( StarfieldCACert ( ) , m_Server , Request , Response ) )
2014-07-14 14:49:31 -04:00
{
return false ;
}
// Check the HTTP status line:
const AString Prefix ( " HTTP/1.1 200 OK " ) ;
AString HexDump ;
if ( Response . compare ( 0 , Prefix . size ( ) , Prefix ) )
{
LOGINFO ( " Failed to get properties for user %s, bad HTTP status line received " , a_UUID . c_str ( ) ) ;
LOGD ( " Response: \n %s " , CreateHexDump ( HexDump , Response . data ( ) , Response . size ( ) , 16 ) . c_str ( ) ) ;
return false ;
}
// Erase the HTTP headers from the response:
size_t idxHeadersEnd = Response . find ( " \r \n \r \n " ) ;
if ( idxHeadersEnd = = AString : : npos )
{
LOGINFO ( " Failed to get properties for user %s, bad HTTP response header received " , a_UUID . c_str ( ) ) ;
LOGD ( " Response: \n %s " , CreateHexDump ( HexDump , Response . data ( ) , Response . size ( ) , 16 ) . c_str ( ) ) ;
return false ;
}
Response . erase ( 0 , idxHeadersEnd + 4 ) ;
// Parse the Json response:
if ( Response . empty ( ) )
{
return false ;
}
2014-07-16 06:21:02 -04:00
2014-07-14 14:49:31 -04:00
Json : : Value root ;
Json : : Reader reader ;
if ( ! reader . parse ( Response , root , false ) )
{
LOGWARNING ( " cAuthenticator: Cannot parse received properties data to JSON! " ) ;
return false ;
}
2014-07-15 19:03:47 -04:00
a_Properties = root [ " properties " ] ;
2014-07-14 14:49:31 -04:00
return true ;
2014-07-15 19:03:47 -04:00
}
2014-07-16 06:21:02 -04:00
*/
2015-05-21 04:26:45 -04:00