1
0
cuberite-2a/source/cAuthenticator.cpp
faketruth f43b65cf53 cClientHandles have a unique ID now to distinguish them
cAuthenticator uses unique client ID for authentication
Changed the kick function used by cAuthenticator to take a client ID instead of name, so the correct user is kicked
Using callback reference instead of pointer in GetChunkData and affiliates
GetChunkData returns false when failed, and true when succeeded
Renamed entity type enums to something prettier
Exposed some functions to Lua

git-svn-id: http://mc-server.googlecode.com/svn/trunk@388 0a769ca7-a7f5-676a-18bf-c427514a06d6
2012-03-09 13:42:28 +00:00

275 lines
6.1 KiB
C++

#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "cAuthenticator.h"
#include "cBlockingTCPLink.h"
#include "cRoot.h"
#include "cServer.h"
#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"),
mServer(DEFAULT_AUTH_SERVER),
mAddress(DEFAULT_AUTH_ADDRESS),
mShouldAuthenticate(true)
{
ReadINI();
}
cAuthenticator::~cAuthenticator()
{
Stop();
}
/// Read custom values from INI
void cAuthenticator::ReadINI(void)
{
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();
}
}
/// 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 (!mShouldAuthenticate)
{
cRoot::Get()->AuthenticateUser(a_ClientID);
return;
}
cCSLock Lock(mCS);
mQueue.push_back(cUser(a_ClientID, a_UserName, a_ServerHash));
mQueueNonempty.Set();
}
void cAuthenticator::Stop(void)
{
mShouldTerminate = true;
mQueueNonempty.Set();
Wait();
}
void cAuthenticator::Execute(void)
{
for (;;)
{
cCSLock Lock(mCS);
while (!mShouldTerminate && (mQueue.size() == 0))
{
cCSUnlock Unlock(Lock);
mQueueNonempty.Wait();
}
if (mShouldTerminate)
{
return;
}
ASSERT(mQueue.size() > 0);
int ClientID = mQueue.front().mClientID;
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))
{
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;
}
Link.SendMessage( AString( "GET " + a_Address + " HTTP/1.0\r\n\r\n" ).c_str());
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());
if (Result.compare("YES") == 0)
{
LOGINFO("Result was \"YES\", so player is authenticated!");
return true;
}
LOGINFO("Result was \"%s\", so player is NOT authenticated!", Result.c_str());
return false;
}