2012-01-29 19:28:19 +00:00
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2011-10-03 18:41:19 +00:00
#include "cAuthenticator.h"
#include "cBlockingTCPLink.h"
2012-02-01 22:38:03 +00:00
#include "cRoot.h"
#include "cServer.h"
2011-10-03 18:41:19 +00:00
#include "../iniFile/iniFile.h"
#include <sstream>
2012-02-01 12:46:44 +00:00
2012-02-01 22:38:03 +00:00
#define DEFAULT_AUTH_SERVER "session.minecraft.net"
#define DEFAULT_AUTH_ADDRESS "/game/checkserver.jsp?user=%USERNAME%&serverId=%SERVERID%"
#define MAX_REDIRECTS 10
2011-10-03 18:41:19 +00:00
2012-02-01 12:46:44 +00:00
2012-02-01 22:38:03 +00:00
cAuthenticator :: cAuthenticator ( void ) :
super ( "cAuthenticator" ),
mServer ( DEFAULT_AUTH_SERVER ),
mAddress ( DEFAULT_AUTH_ADDRESS ),
mShouldAuthenticate ( true )
2011-10-03 18:41:19 +00:00
{
2012-02-01 22:38:03 +00:00
ReadINI ();
2011-10-03 18:41:19 +00:00
}
2012-02-01 22:38:03 +00:00
2012-02-13 21:47:03 +00:00
cAuthenticator ::~ cAuthenticator ()
{
2012-02-15 21:47:21 +00:00
Stop ();
2012-02-13 21:47:03 +00:00
}
2012-02-01 22:38:03 +00:00
/// Read custom values from INI
void cAuthenticator :: ReadINI ( void )
2011-10-03 18:41:19 +00:00
{
2012-02-01 22:38:03 +00:00
cIniFile IniFile ( "settings.ini" );
if ( ! IniFile . ReadFile ())
{
return ;
}
mServer = IniFile . GetValue ( "Authentication" , "Server" );
mAddress = IniFile . GetValue ( "Authentication" , "Address" );
mShouldAuthenticate = IniFile . GetValueB ( "Authentication" , "Authenticate" , true );
bool bSave = false ;
if ( mServer . length () == 0 )
{
mServer = DEFAULT_AUTH_SERVER ;
IniFile . SetValue ( "Authentication" , "Server" , mServer );
bSave = true ;
}
if ( mAddress . length () == 0 )
{
mAddress = DEFAULT_AUTH_ADDRESS ;
IniFile . SetValue ( "Authentication" , "Address" , mAddress );
bSave = true ;
}
if ( bSave )
{
IniFile . SetValueB ( "Authentication" , "Authenticate" , mShouldAuthenticate );
IniFile . WriteFile ();
}
2011-10-03 18:41:19 +00:00
}
2012-02-01 22:38:03 +00:00
/// Queues a request for authenticating a user. If the auth fails, the user is kicked
void cAuthenticator :: Authenticate ( const AString & iUserName , const AString & iServerID )
2011-10-03 18:41:19 +00:00
{
2012-02-01 22:38:03 +00:00
if ( ! mShouldAuthenticate )
{
cRoot :: Get () -> AuthenticateUser ( iUserName );
return ;
}
2011-10-03 18:41:19 +00:00
2012-02-01 22:38:03 +00:00
cCSLock Lock ( mCS );
mQueue . push_back ( cUser ( iUserName , iServerID ));
mQueueNonempty . Set ();
}
2012-02-15 21:47:21 +00:00
void cAuthenticator :: Stop ( void )
{
mShouldTerminate = true ;
mQueueNonempty . Set ();
Wait ();
}
2012-02-01 22:38:03 +00:00
void cAuthenticator :: Execute ( void )
{
2012-02-18 17:53:22 +00:00
for (;;)
2011-10-03 18:41:19 +00:00
{
2012-02-01 22:38:03 +00:00
cCSLock Lock ( mCS );
while ( ! mShouldTerminate && ( mQueue . size () == 0 ))
2011-10-03 18:41:19 +00:00
{
2012-02-01 22:38:03 +00:00
cCSUnlock Unlock ( Lock );
mQueueNonempty . Wait ();
2011-10-03 18:41:19 +00:00
}
2012-02-01 22:38:03 +00:00
if ( mShouldTerminate )
{
return ;
}
2012-02-19 23:00:00 +00:00
ASSERT ( mQueue . size () > 0 );
2012-02-01 22:38:03 +00:00
AString UserName = mQueue . front (). mName ;
AString ActualAddress = mAddress ;
ReplaceString ( ActualAddress , "%USERNAME%" , UserName );
ReplaceString ( ActualAddress , "%SERVERID%" , cRoot :: Get () -> GetServer () -> GetServerID ());
mQueue . pop_front ();
Lock . Unlock ();
if ( ! AuthFromAddress ( mServer , ActualAddress , UserName ))
2011-10-03 18:41:19 +00:00
{
2012-02-01 22:38:03 +00:00
cRoot :: Get () -> KickUser ( UserName , "auth failed" );
2011-10-03 18:41:19 +00:00
}
else
{
2012-02-01 22:38:03 +00:00
cRoot :: Get () -> AuthenticateUser ( UserName );
2011-10-03 18:41:19 +00:00
}
2012-02-18 17:53:22 +00:00
} // for (-ever)
2012-02-01 22:38:03 +00:00
}
2011-10-03 18:41:19 +00:00
2012-02-01 22:38:03 +00:00
bool cAuthenticator :: AuthFromAddress ( const AString & iServer , const AString & iAddress , const AString & iUserName , int iLevel )
{
// 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 ( iServer . c_str (), 80 ))
2011-10-03 18:41:19 +00:00
{
2012-02-01 22:38:03 +00:00
LOGERROR ( "cAuthenticator: cannot connect to auth server \" %s \" , kicking user \" %s \" " , iServer . c_str (), iUserName . c_str ());
2011-10-03 18:41:19 +00:00
return false ;
}
2012-02-01 22:38:03 +00:00
Link . SendMessage ( AString ( "GET " + iAddress + " HTTP/1.0 \r\n\r\n " ). c_str ());
AString DataRecvd ;
Link . ReceiveData ( DataRecvd );
Link . CloseSocket ();
2011-10-03 18:41:19 +00:00
2012-02-01 22:38:03 +00:00
std :: stringstream ss ( DataRecvd );
2011-10-03 18:41:19 +00:00
2012-02-01 22:38:03 +00:00
// Parse the data received:
2011-10-03 18:41:19 +00:00
std :: string temp ;
ss >> temp ;
bool bRedirect = false ;
bool bOK = false ;
2012-02-01 22:38:03 +00:00
if (( temp . compare ( "HTTP/1.1" ) == 0 ) || ( temp . compare ( "HTTP/1.0" ) == 0 ))
2011-10-03 18:41:19 +00:00
{
int code ;
ss >> code ;
2012-02-01 22:38:03 +00:00
if ( code == 302 )
2011-10-03 18:41:19 +00:00
{
// redirect blabla
LOGINFO ( "Need to redirect!" );
2012-02-01 22:38:03 +00:00
if ( iLevel > MAX_REDIRECTS )
{
LOGERROR ( "cAuthenticator: received too many levels of redirection from auth server \" %s \" for user \" %s \" , bailing out and kicking the user" , iServer . c_str (), iUserName . c_str ());
return false ;
}
2011-10-03 18:41:19 +00:00
bRedirect = true ;
}
2012-02-01 22:38:03 +00:00
else if ( code == 200 )
2011-10-03 18:41:19 +00:00
{
LOGINFO ( "Got 200 OK :D" );
bOK = true ;
}
}
else
2012-02-01 22:38:03 +00:00
{
LOGERROR ( "cAuthenticator: cannot parse auth reply from server \" %s \" for user \" %s \" , kicking the user." , iServer . c_str (), iUserName . c_str ());
2011-10-03 18:41:19 +00:00
return false ;
2012-02-01 22:38:03 +00:00
}
2011-10-03 18:41:19 +00:00
if ( bRedirect )
{
2012-02-01 22:38:03 +00:00
AString Location ;
2011-10-03 18:41:19 +00:00
// Search for "Location:"
bool bFoundLocation = false ;
while ( ! bFoundLocation && ss . good () )
{
char c = 0 ;
while ( c != '\n' )
{
ss . get ( c );
}
2012-02-01 22:38:03 +00:00
AString Name ;
2011-10-03 18:41:19 +00:00
ss >> Name ;
2012-02-01 22:38:03 +00:00
if ( Name . compare ( "Location:" ) == 0 )
2011-10-03 18:41:19 +00:00
{
bFoundLocation = true ;
ss >> Location ;
}
}
2012-02-01 22:38:03 +00:00
if ( ! bFoundLocation )
2011-10-03 18:41:19 +00:00
{
2012-02-01 22:38:03 +00:00
LOGERROR ( "cAuthenticator: received invalid redirection from auth server \" %s \" for user \" %s \" , kicking user." , iServer . c_str (), iUserName . c_str ());
2011-10-03 18:41:19 +00:00
return false ;
}
2012-02-01 22:38:03 +00:00
Location = Location . substr ( strlen ( "http://" ), std :: string :: npos ); // Strip http://
2011-10-03 18:41:19 +00:00
std :: string Server = Location . substr ( 0 , Location . find ( "/" ) ); // Only leave server address
2012-02-01 22:38:03 +00:00
Location = Location . substr ( Server . length (), std :: string :: npos );
return AuthFromAddress ( Server , Location , iUserName , iLevel + 1 );
2011-10-03 18:41:19 +00:00
}
2012-02-01 22:38:03 +00:00
if ( ! bOK )
2011-10-03 18:41:19 +00:00
{
2012-02-01 22:38:03 +00:00
LOGERROR ( "cAuthenticator: received an error from auth server \" %s \" for user \" %s \" , kicking user." , iServer . c_str (), iUserName . c_str ());
return false ;
}
2011-10-03 18:41:19 +00:00
2012-02-01 22:38:03 +00:00
// 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' )
2011-10-03 18:41:19 +00:00
{
2012-02-01 22:38:03 +00:00
ss . get ( c );
2011-10-03 18:41:19 +00:00
}
2012-02-01 22:38:03 +00:00
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." , iServer . c_str (), iUserName . c_str ());
return false ;
}
2011-10-03 18:41:19 +00:00
2012-02-01 22:38:03 +00:00
std :: string Result ;
ss >> Result ;
LOGINFO ( "Got result: %s" , Result . c_str ());
if ( Result . compare ( "YES" ) == 0 )
{
LOGINFO ( "Result was \" YES \" , so player is authenticated!" );
return true ;
2011-10-03 18:41:19 +00:00
}
2012-02-01 22:38:03 +00:00
LOGINFO ( "Result was \" %s \" , so player is NOT authenticated!" , Result . c_str ());
2011-10-03 18:41:19 +00:00
return false ;
}
2012-02-01 22:38:03 +00:00