2012-06-14 13:06:06 +00:00
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2012-09-23 22:09:57 +00:00
#include "Authenticator.h"
2012-09-23 21:23:33 +00:00
#include "OSSupport/BlockingTCPLink.h"
2012-09-23 22:09:57 +00:00
#include "Root.h"
#include "Server.h"
2012-06-14 13:06:06 +00:00
#include "../iniFile/iniFile.h"
#include <sstream>
#define DEFAULT_AUTH_SERVER "session.minecraft.net"
#define DEFAULT_AUTH_ADDRESS "/game/checkserver.jsp?user=%USERNAME%&serverId=%SERVERID%"
#define MAX_REDIRECTS 10
cAuthenticator :: cAuthenticator ( void ) :
super ( "cAuthenticator" ),
m_Server ( DEFAULT_AUTH_SERVER ),
m_Address ( DEFAULT_AUTH_ADDRESS ),
m_ShouldAuthenticate ( true )
{
ReadINI ();
}
cAuthenticator ::~ cAuthenticator ()
{
Stop ();
}
/// Read custom values from INI
void cAuthenticator :: ReadINI ( void )
{
cIniFile IniFile ( "settings.ini" );
if ( ! IniFile . ReadFile ())
{
return ;
}
m_Server = IniFile . GetValue ( "Authentication" , "Server" );
m_Address = IniFile . GetValue ( "Authentication" , "Address" );
m_ShouldAuthenticate = IniFile . GetValueB ( "Authentication" , "Authenticate" , true );
bool bSave = false ;
if ( m_Server . length () == 0 )
{
m_Server = DEFAULT_AUTH_SERVER ;
IniFile . SetValue ( "Authentication" , "Server" , m_Server );
bSave = true ;
}
if ( m_Address . length () == 0 )
{
m_Address = DEFAULT_AUTH_ADDRESS ;
IniFile . SetValue ( "Authentication" , "Address" , m_Address );
bSave = true ;
}
if ( bSave )
{
IniFile . SetValueB ( "Authentication" , "Authenticate" , m_ShouldAuthenticate );
IniFile . WriteFile ();
}
}
/// Queues a request for authenticating a user. If the auth fails, the user is kicked
void cAuthenticator :: Authenticate ( int a_ClientID , const AString & a_UserName , const AString & a_ServerHash )
{
if ( ! m_ShouldAuthenticate )
{
cRoot :: Get () -> AuthenticateUser ( a_ClientID );
return ;
}
cCSLock Lock ( m_CS );
m_Queue . push_back ( cUser ( a_ClientID , a_UserName , a_ServerHash ));
m_QueueNonempty . Set ();
}
2013-10-07 10:45:03 +02:00
void cAuthenticator :: Start ( void )
{
m_ShouldTerminate = false ;
super :: Start ();
}
2012-06-14 13:06:06 +00:00
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 ());
2012-09-06 17:36:59 +00:00
int ClientID = m_Queue . front (). m_ClientID ;
AString UserName = m_Queue . front (). m_Name ;
2012-06-14 13:06:06 +00:00
AString ActualAddress = m_Address ;
ReplaceString ( ActualAddress , "%USERNAME%" , UserName );
2012-09-06 17:36:59 +00:00
ReplaceString ( ActualAddress , "%SERVERID%" , m_Queue . front (). m_ServerID );
2012-06-14 13:06:06 +00:00
m_Queue . pop_front ();
Lock . Unlock ();
if ( ! AuthFromAddress ( m_Server , ActualAddress , UserName ))
{
cRoot :: Get () -> KickUser ( ClientID , "Failed to authenticate account!" );
}
else
{
cRoot :: Get () -> AuthenticateUser ( ClientID );
}
} // for (-ever)
}
bool cAuthenticator :: AuthFromAddress ( const AString & a_Server , const AString & a_Address , const AString & a_UserName , int a_Level /* = 1 */ )
{
// Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep)
cBlockingTCPLink Link ;
if ( ! Link . Connect ( a_Server . c_str (), 80 ))
{
LOGERROR ( "cAuthenticator: cannot connect to auth server \" %s \" , kicking user \" %s \" " , a_Server . c_str (), a_Server . c_str ());
return false ;
}
2012-06-16 17:02:25 +00:00
Link . SendMessage ( AString ( "GET " + a_Address + " HTTP/1.1 \r\n " ). c_str ());
Link . SendMessage ( AString ( "User-Agent: MCServer \r\n " ). c_str ());
2012-07-10 07:16:42 +00:00
Link . SendMessage ( AString ( "Host: " + a_Server + " \r\n " ). c_str ());
//Link.SendMessage( AString( "Host: session.minecraft.net\r\n" ).c_str());
2012-06-16 17:02:25 +00:00
Link . SendMessage ( AString ( "Accept: */* \r\n " ). c_str ());
2012-07-10 07:16:42 +00:00
Link . SendMessage ( AString ( "Connection: close \r\n " ). c_str ()); //Close so we don´ t have to mess with the Content-Length :)
2012-06-16 17:02:25 +00:00
Link . SendMessage ( AString ( " \r\n " ). c_str ());
2012-06-14 13:06:06 +00:00
AString DataRecvd ;
Link . ReceiveData ( DataRecvd );
Link . CloseSocket ();
std :: stringstream ss ( DataRecvd );
// Parse the data received:
std :: string temp ;
ss >> temp ;
bool bRedirect = false ;
bool bOK = false ;
if (( temp . compare ( "HTTP/1.1" ) == 0 ) || ( temp . compare ( "HTTP/1.0" ) == 0 ))
{
int code ;
ss >> code ;
if ( code == 302 )
{
// redirect blabla
LOGINFO ( "Need to redirect!" );
if ( a_Level > MAX_REDIRECTS )
{
LOGERROR ( "cAuthenticator: received too many levels of redirection from auth server \" %s \" for user \" %s \" , bailing out and kicking the user" , a_Server . c_str (), a_UserName . c_str ());
return false ;
}
bRedirect = true ;
}
else if ( code == 200 )
{
LOGINFO ( "Got 200 OK :D" );
bOK = true ;
}
}
else
{
LOGERROR ( "cAuthenticator: cannot parse auth reply from server \" %s \" for user \" %s \" , kicking the user." , a_Server . c_str (), a_UserName . c_str ());
return false ;
}
if ( bRedirect )
{
AString Location ;
// Search for "Location:"
bool bFoundLocation = false ;
while ( ! bFoundLocation && ss . good () )
{
char c = 0 ;
while ( c != '\n' )
{
ss . get ( c );
}
AString Name ;
ss >> Name ;
if ( Name . compare ( "Location:" ) == 0 )
{
bFoundLocation = true ;
ss >> Location ;
}
}
if ( ! bFoundLocation )
{
LOGERROR ( "cAuthenticator: received invalid redirection from auth server \" %s \" for user \" %s \" , kicking user." , a_Server . c_str (), a_UserName . c_str ());
return false ;
}
Location = Location . substr ( strlen ( "http://" ), std :: string :: npos ); // Strip http://
std :: string Server = Location . substr ( 0 , Location . find ( "/" ) ); // Only leave server address
Location = Location . substr ( Server . length (), std :: string :: npos );
return AuthFromAddress ( Server , Location , a_UserName , a_Level + 1 );
}
if ( ! bOK )
{
LOGERROR ( "cAuthenticator: received an error from auth server \" %s \" for user \" %s \" , kicking user." , a_Server . c_str (), a_UserName . c_str ());
return false ;
}
// Header says OK, so receive the rest.
// Go past header, double \n means end of headers
char c = 0 ;
while ( ss . good ())
{
while ( c != '\n' )
{
ss . get ( c );
}
ss . get ( c );
if ( c == '\n' || c == '\r' || ss . peek () == '\r' || ss . peek () == '\n' )
break ;
}
if ( ! ss . good ())
{
LOGERROR ( "cAuthenticator: error while parsing response body from auth server \" %s \" for user \" %s \" , kicking user." , a_Server . c_str (), a_UserName . c_str ());
return false ;
}
std :: string Result ;
ss >> Result ;
LOGINFO ( "Got result: %s" , Result . c_str ());
2012-07-10 07:16:42 +00:00
//if (Result.compare("3") == 0) // FIXME: Quick and dirty hack to support auth
//Lapayo: Wtf 3?
if ( Result . compare ( "YES" ) == 0 ) //Works well
2012-06-14 13:06:06 +00:00
{
LOGINFO ( "Result was \" YES \" , so player is authenticated!" );
return true ;
}
2012-07-10 07:16:42 +00:00
2012-06-14 13:06:06 +00:00
LOGINFO ( "Result was \" %s \" , so player is NOT authenticated!" , Result . c_str ());
return false ;
}