2014-04-13 11:04:56 +00:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
# include "Authenticator.h"
2014-07-29 15:45:55 +00:00
# include "MojangAPI.h"
2014-04-14 20:52:59 +00:00
# include "../Root.h"
# include "../Server.h"
# include "../ClientHandle.h"
2014-04-13 11:04:56 +00:00
2014-10-23 13:15:10 +00:00
# include "../IniFile.h"
2014-04-13 11:04:56 +00:00
# include "json/json.h"
2014-04-27 20:28:14 +00:00
# include "PolarSSL++/BlockingSslClientSocket.h"
2014-04-13 11:04:56 +00:00
# define DEFAULT_AUTH_SERVER "sessionserver.mojang.com"
# define DEFAULT_AUTH_ADDRESS " / session / minecraft / hasJoined?username=%USERNAME%&serverId=%SERVERID%"
cAuthenticator : : cAuthenticator ( void ) :
2014-04-14 20:52:59 +00:00
super ( " cAuthenticator " ) ,
m_Server ( DEFAULT_AUTH_SERVER ) ,
m_Address ( DEFAULT_AUTH_ADDRESS ) ,
m_ShouldAuthenticate ( true )
2014-04-13 11:04:56 +00:00
{
}
cAuthenticator : : ~ cAuthenticator ( )
{
Stop ( ) ;
}
void cAuthenticator : : ReadINI ( cIniFile & IniFile )
{
2014-07-28 10:37:48 +00:00
m_Server = IniFile . GetValueSet ( " Authentication " , " Server " , DEFAULT_AUTH_SERVER ) ;
m_Address = IniFile . GetValueSet ( " Authentication " , " Address " , DEFAULT_AUTH_ADDRESS ) ;
2014-04-13 11:04:56 +00:00
m_ShouldAuthenticate = IniFile . GetValueSetB ( " Authentication " , " Authenticate " , true ) ;
}
void cAuthenticator : : Authenticate ( int a_ClientID , const AString & a_UserName , const AString & a_ServerHash )
{
if ( ! m_ShouldAuthenticate )
{
2014-07-15 23:03:47 +00:00
Json : : Value Value ;
cRoot : : Get ( ) - > AuthenticateUser ( a_ClientID , a_UserName , cClientHandle : : GenerateOfflineUUID ( a_UserName ) , Value ) ;
2014-04-13 11:04:56 +00:00
return ;
}
cCSLock LOCK ( m_CS ) ;
m_Queue . push_back ( cUser ( a_ClientID , a_UserName , a_ServerHash ) ) ;
m_QueueNonempty . Set ( ) ;
}
void cAuthenticator : : Start ( cIniFile & IniFile )
{
ReadINI ( IniFile ) ;
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 19:44:58 +00: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 11:04:56 +00:00
m_Queue . pop_front ( ) ;
Lock . Unlock ( ) ;
AString NewUserName = UserName ;
AString UUID ;
2014-07-16 10:21:02 +00:00
Json : : Value Properties ;
if ( AuthWithYggdrasil ( NewUserName , ServerID , UUID , Properties ) )
2014-04-13 11:04:56 +00:00
{
2014-07-16 10:21:02 +00:00
LOGINFO ( " User %s authenticated with UUID %s " , NewUserName . c_str ( ) , UUID . c_str ( ) ) ;
2014-07-14 18:49:31 +00:00
cRoot : : Get ( ) - > AuthenticateUser ( ClientID , NewUserName , UUID , Properties ) ;
2014-04-13 11:04:56 +00:00
}
else
{
cRoot : : Get ( ) - > KickUser ( ClientID , " Failed to authenticate account! " ) ;
}
} // for (-ever)
}
2014-07-16 10:21:02 +00:00
bool cAuthenticator : : AuthWithYggdrasil ( AString & a_UserName , const AString & a_ServerId , AString & a_UUID , Json : : Value & a_Properties )
2014-07-14 18:49:31 +00:00
{
2014-07-17 20:50:58 +00:00
LOGD ( " Trying to authenticate user %s " , a_UserName . c_str ( ) ) ;
2014-07-14 18:49:31 +00: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 15:45:55 +00:00
if ( ! cMojangAPI : : SecureRequest ( m_Server , Request , Response ) )
2014-07-14 18:49:31 +00:00
{
return false ;
}
2014-04-13 11:04:56 +00:00
2014-04-14 20:52:59 +00:00
// Check the HTTP status line:
2014-07-14 18:49:31 +00:00
const AString Prefix ( " HTTP/1.1 200 OK " ) ;
2014-04-14 20:52:59 +00:00
AString HexDump ;
2014-07-14 18:49:31 +00:00
if ( Response . compare ( 0 , Prefix . size ( ) , Prefix ) )
2014-04-14 20:52:59 +00:00
{
2014-07-14 18:49:31 +00: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 11:04:56 +00:00
return false ;
2014-04-14 20:52:59 +00:00
}
2014-04-13 11:04:56 +00:00
2014-04-14 20:52:59 +00:00
// Erase the HTTP headers from the response:
size_t idxHeadersEnd = Response . find ( " \r \n \r \n " ) ;
if ( idxHeadersEnd = = AString : : npos )
2014-04-13 11:04:56 +00:00
{
2014-07-14 18:49:31 +00: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 20:52:59 +00:00
return false ;
2014-04-13 11:04:56 +00:00
}
2014-04-14 20:52:59 +00:00
Response . erase ( 0 , idxHeadersEnd + 4 ) ;
2014-04-13 11:04:56 +00:00
2014-04-14 20:52:59 +00:00
// Parse the Json response:
if ( Response . empty ( ) )
{
2014-04-13 11:04:56 +00:00
return false ;
2014-04-14 20:52:59 +00:00
}
2014-04-13 11:04:56 +00:00
Json : : Value root ;
Json : : Reader reader ;
2014-04-14 20:52:59 +00:00
if ( ! reader . parse ( Response , root , false ) )
2014-04-13 11:04:56 +00:00
{
2014-07-14 18:49:31 +00:00
LOGWARNING ( " cAuthenticator: Cannot parse received data (authentication) to JSON! " ) ;
2014-04-13 11:04:56 +00:00
return false ;
}
a_UserName = root . get ( " name " , " Unknown " ) . asString ( ) ;
2014-07-30 11:52:51 +00:00
a_UUID = cMojangAPI : : MakeUUIDShort ( root . get ( " id " , " " ) . asString ( ) ) ;
2014-07-16 10:21:02 +00:00
a_Properties = root [ " properties " ] ;
2014-07-29 15:45:55 +00:00
2014-08-03 19:32:20 +00:00
// Store the player's profile in the MojangAPI caches:
cRoot : : Get ( ) - > GetMojangAPI ( ) . AddPlayerProfile ( a_UserName , a_UUID , a_Properties ) ;
2014-07-16 10:21:02 +00:00
2014-04-13 11:04:56 +00:00
return true ;
}
2014-07-16 10:21:02 +00: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 23:03:47 +00:00
bool cAuthenticator : : GetPlayerProperties ( const AString & a_UUID , Json : : Value & a_Properties )
2014-07-14 18:49:31 +00: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 04:38:55 +00:00
if ( ! ConnectSecurelyToAddress ( StarfieldCACert ( ) , m_Server , Request , Response ) )
2014-07-14 18:49:31 +00: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 10:21:02 +00:00
2014-07-14 18:49:31 +00: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 23:03:47 +00:00
a_Properties = root [ " properties " ] ;
2014-07-14 18:49:31 +00:00
return true ;
2014-07-15 23:03:47 +00:00
}
2014-07-16 10:21:02 +00:00
*/