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" ),
2012-03-10 17:37:00 +00:00
m_Server ( DEFAULT_AUTH_SERVER ),
m_Address ( DEFAULT_AUTH_ADDRESS ),
m_ShouldAuthenticate ( 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 ;
}
2012-03-10 17:37:00 +00:00
m_Server = IniFile . GetValue ( "Authentication" , "Server" );
m_Address = IniFile . GetValue ( "Authentication" , "Address" );
m_ShouldAuthenticate = IniFile . GetValueB ( "Authentication" , "Authenticate" , true );
2012-02-01 22:38:03 +00:00
bool bSave = false ;
2012-03-10 17:37:00 +00:00
if ( m_Server . length () == 0 )
2012-02-01 22:38:03 +00:00
{
2012-03-10 17:37:00 +00:00
m_Server = DEFAULT_AUTH_SERVER ;
IniFile . SetValue ( "Authentication" , "Server" , m_Server );
2012-02-01 22:38:03 +00:00
bSave = true ;
}
2012-03-10 17:37:00 +00:00
if ( m_Address . length () == 0 )
2012-02-01 22:38:03 +00:00
{
2012-03-10 17:37:00 +00:00
m_Address = DEFAULT_AUTH_ADDRESS ;
IniFile . SetValue ( "Authentication" , "Address" , m_Address );
2012-02-01 22:38:03 +00:00
bSave = true ;
}
if ( bSave )
{
2012-03-10 17:37:00 +00:00
IniFile . SetValueB ( "Authentication" , "Authenticate" , m_ShouldAuthenticate );
2012-02-01 22:38:03 +00:00
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
2012-03-09 13:42:28 +00:00
void cAuthenticator :: Authenticate ( int a_ClientID , const AString & a_UserName , const AString & a_ServerHash )
2011-10-03 18:41:19 +00:00
{
2012-03-10 17:37:00 +00:00
if ( ! m_ShouldAuthenticate )
2012-02-01 22:38:03 +00:00
{
2012-03-09 13:42:28 +00:00
cRoot :: Get () -> AuthenticateUser ( a_ClientID );
2012-02-01 22:38:03 +00:00
return ;
}
2011-10-03 18:41:19 +00:00
2012-03-10 17:37:00 +00:00
cCSLock Lock ( m_CS );
m_Queue . push_back ( cUser ( a_ClientID , a_UserName , a_ServerHash ));
m_QueueNonempty . Set ();
2012-02-01 22:38:03 +00:00
}
2012-02-15 21:47:21 +00:00
void cAuthenticator :: Stop ( void )
{
2012-03-10 17:37:00 +00:00
m_ShouldTerminate = true ;
m_QueueNonempty . Set ();
2012-02-15 21:47:21 +00:00
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-03-10 17:37:00 +00:00
cCSLock Lock ( m_CS );
while ( ! m_ShouldTerminate && ( m_Queue . size () == 0 ))
2011-10-03 18:41:19 +00:00
{
2012-02-01 22:38:03 +00:00
cCSUnlock Unlock ( Lock );
2012-03-10 17:37:00 +00:00
m_QueueNonempty . Wait ();
2011-10-03 18:41:19 +00:00
}
2012-03-10 17:37:00 +00:00
if ( m_ShouldTerminate )
2012-02-01 22:38:03 +00:00
{
return ;
}
2012-05-25 07:18:52 +00:00
ASSERT ( ! m_Queue . empty ());
2012-02-01 22:38:03 +00:00
2012-03-10 17:37:00 +00:00
int ClientID = m_Queue . front (). mClientID ;
AString UserName = m_Queue . front (). mName ;
AString ActualAddress = m_Address ;
2012-02-01 22:38:03 +00:00
ReplaceString ( ActualAddress , "%USERNAME%" , UserName );
ReplaceString ( ActualAddress , "%SERVERID%" , cRoot :: Get () -> GetServer () -> GetServerID ());
2012-03-10 17:37:00 +00:00
m_Queue . pop_front ();
2012-02-01 22:38:03 +00:00
Lock . Unlock ();
2012-03-10 17:37:00 +00:00
if ( ! AuthFromAddress ( m_Server , ActualAddress , UserName ))
2011-10-03 18:41:19 +00:00
{
2012-03-09 13:42:28 +00:00
cRoot :: Get () -> KickUser ( ClientID , "Failed to authenticate account!" );
2011-10-03 18:41:19 +00:00
}
else
{
2012-03-09 13:42:28 +00:00
cRoot :: Get () -> AuthenticateUser ( ClientID );
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
2012-03-09 13:42:28 +00:00
bool cAuthenticator :: AuthFromAddress ( const AString & a_Server , const AString & a_Address , const AString & a_UserName , int a_Level /* = 1 */ )
2012-02-01 22:38:03 +00:00
{
// Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep)
cBlockingTCPLink Link ;
2012-03-09 13:42:28 +00:00
if ( ! Link . Connect ( a_Server . c_str (), 80 ))
2011-10-03 18:41:19 +00:00
{
2012-03-09 13:42:28 +00:00
LOGERROR ( "cAuthenticator: cannot connect to auth server \" %s \" , kicking user \" %s \" " , a_Server . c_str (), a_Server . c_str ());
2011-10-03 18:41:19 +00:00
return false ;
}
2012-02-01 22:38:03 +00:00
2012-03-09 13:42:28 +00:00
Link . SendMessage ( AString ( "GET " + a_Address + " HTTP/1.0 \r\n\r\n " ). c_str ());
2012-02-01 22:38:03 +00:00
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-03-09 13:42:28 +00:00
if ( a_Level > MAX_REDIRECTS )
2012-02-01 22:38:03 +00:00
{
2012-03-09 13:42:28 +00:00
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 ());
2012-02-01 22:38:03 +00:00
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
{
2012-03-09 13:42:28 +00:00
LOGERROR ( "cAuthenticator: cannot parse auth reply from server \" %s \" for user \" %s \" , kicking the user." , a_Server . c_str (), a_UserName . 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-03-09 13:42:28 +00:00
LOGERROR ( "cAuthenticator: received invalid redirection from auth server \" %s \" for user \" %s \" , kicking user." , a_Server . c_str (), a_UserName . 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 );
2012-03-09 13:42:28 +00:00
return AuthFromAddress ( Server , Location , a_UserName , a_Level + 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-03-09 13:42:28 +00:00
LOGERROR ( "cAuthenticator: received an error from auth server \" %s \" for user \" %s \" , kicking user." , a_Server . c_str (), a_UserName . c_str ());
2012-02-01 22:38:03 +00:00
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 ())
{
2012-03-09 13:42:28 +00:00
LOGERROR ( "cAuthenticator: error while parsing response body from auth server \" %s \" for user \" %s \" , kicking user." , a_Server . c_str (), a_UserName . c_str ());
2012-02-01 22:38:03 +00:00
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