* rewrote the realm/world login code.

-> reconnecting does work now, finally.
* updated conf setting: reconnect=0: dont reconnect; reconnect=X: reconnect after X msecs.
* misc code cleanups, crash fixes, and overall improved stability.
* splitted RealmSocket into a RealmSession and the original RealmSocket code. better read-/useabilty because now its handled as WorldSession and related.
This commit is contained in:
False.Genesis 2007-05-11 20:55:38 +00:00
parent 95353eb132
commit 6c4ae79a87
20 changed files with 796 additions and 638 deletions

View File

@ -17,7 +17,10 @@ debug=0
exitonerror=0
// reconnect on failure/disconnect
reconnect=0
// 0 = dont't reconnect
// everything else: delay (in ms) until the next connection attempt.
// default: 5000 ms (5 secs)
reconnect=5000
// 0 - show none
// 1 - show only known/handled
@ -33,13 +36,14 @@ showopcodes=3
hidefreqopcodes=1
// the IP or hostname the wow server is running on
// the IP or hostname the realm server is running on
realmlist=localhost
// port on which the realm server is listening (default=3724)
realmport=3724
// PseuWoW will login on this realm
// Case sensitive!
realmname=My WoW Realm
// your account name
@ -48,8 +52,9 @@ accname=test
// your account password
accpass=test
// the character name PseuWoW should choose to enter the world
charname=PseuWoW
// the character name PseuWoW should choose to enter the world.
// case sensitive!
charname=Pseuwow
// Client emulation configuration
@ -104,3 +109,4 @@ enablegui=0

View File

@ -23,7 +23,7 @@ name=Shaman
name=Mage
[9]
name=Paladin
name=Warlock
[10]
name=UNKNOWN CLASS(10)

View File

@ -30,7 +30,8 @@ DefScriptPackage::DefScriptPackage()
DefScriptPackage::~DefScriptPackage()
{
delete _eventmgr;
if(_eventmgr)
delete _eventmgr;
Clear();
}

View File

@ -56,7 +56,7 @@ DefReturnResult DefScriptPackage::SCpause(CmdSet& Set){
}
DefReturnResult DefScriptPackage::SCSendChatMessage(CmdSet& Set){
if(!(((PseuInstance*)parentMethod)->GetWSession() && ((PseuInstance*)parentMethod)->GetWSession()->IsValid()))
if(!(((PseuInstance*)parentMethod)->GetWSession()))
{
logerror("Invalid Script call: SCSendChatMessage: WorldSession not valid");
DEF_RETURN_ERROR;
@ -88,7 +88,7 @@ DefReturnResult DefScriptPackage::SCSendChatMessage(CmdSet& Set){
DefReturnResult DefScriptPackage::SCsavecache(CmdSet& Set){
((PseuInstance*)parentMethod)->SaveAllCache();
std::stringstream str;
if(((PseuInstance*)parentMethod)->GetWSession() && ((PseuInstance*)parentMethod)->GetWSession()->IsValid())
if(((PseuInstance*)parentMethod)->GetWSession())
{
str << "Cache saved. [ ";
str << ((PseuInstance*)parentMethod)->GetWSession()->plrNameCache.GetSize();
@ -105,7 +105,7 @@ DefReturnResult DefScriptPackage::SCsavecache(CmdSet& Set){
DefReturnResult DefScriptPackage::SCemote(CmdSet& Set){
if(Set.defaultarg.empty())
return false;
if(!(((PseuInstance*)parentMethod)->GetWSession() && ((PseuInstance*)parentMethod)->GetWSession()->IsValid()))
if(!(((PseuInstance*)parentMethod)->GetWSession()))
{
logerror("Invalid Script call: SCEmote: WorldSession not valid");
DEF_RETURN_ERROR;
@ -141,7 +141,7 @@ DefReturnResult DefScriptPackage::SCfollow(CmdSet& Set)
DefReturnResult DefScriptPackage::SCjoinchannel(CmdSet& Set){
if(Set.defaultarg.empty())
return false;
if(!(((PseuInstance*)parentMethod)->GetWSession() && ((PseuInstance*)parentMethod)->GetWSession()->IsValid()))
if(!(((PseuInstance*)parentMethod)->GetWSession()))
{
logerror("Invalid Script call: SCjoinchannel: WorldSession not valid");
DEF_RETURN_ERROR;
@ -153,7 +153,7 @@ DefReturnResult DefScriptPackage::SCjoinchannel(CmdSet& Set){
DefReturnResult DefScriptPackage::SCleavechannel(CmdSet& Set){
if(Set.defaultarg.empty())
return false;
if(!(((PseuInstance*)parentMethod)->GetWSession() && ((PseuInstance*)parentMethod)->GetWSession()->IsValid()))
if(!(((PseuInstance*)parentMethod)->GetWSession()))
{
logerror("Invalid Script call: SCleavechannel: WorldSession not valid");
DEF_RETURN_ERROR;
@ -214,7 +214,7 @@ DefReturnResult DefScriptPackage::SCcastspell(CmdSet& Set)
{
if(Set.defaultarg.empty())
return false;
if(!(((PseuInstance*)parentMethod)->GetWSession() && ((PseuInstance*)parentMethod)->GetWSession()->IsValid()))
if(!(((PseuInstance*)parentMethod)->GetWSession()))
{
logerror("Invalid Script call: SCcastspell: WorldSession not valid");
DEF_RETURN_ERROR;
@ -236,7 +236,7 @@ DefReturnResult DefScriptPackage::SCqueryitem(CmdSet& Set){
if(!id)
return false;
if(!(((PseuInstance*)parentMethod)->GetWSession() && ((PseuInstance*)parentMethod)->GetWSession()->IsValid()))
if(!(((PseuInstance*)parentMethod)->GetWSession()))
{
logerror("Invalid Script call: SCqueryitem: WorldSession not valid");
DEF_RETURN_ERROR;
@ -250,7 +250,7 @@ DefReturnResult DefScriptPackage::SCtarget(CmdSet& Set)
// TODO: special targets: _self _pet _nearest ...
DefReturnResult r;
if(!(((PseuInstance*)parentMethod)->GetWSession() && ((PseuInstance*)parentMethod)->GetWSession()->IsValid()))
if(!(((PseuInstance*)parentMethod)->GetWSession()))
{
logerror("Invalid Script call: SCtarget: WorldSession not valid");
DEF_RETURN_ERROR;
@ -360,7 +360,7 @@ DefReturnResult DefScriptPackage::SCGetScpValue(CmdSet& Set)
DefReturnResult DefScriptPackage::SCGetPlayerGuid(CmdSet& Set)
{
if(!(((PseuInstance*)parentMethod)->GetWSession() && ((PseuInstance*)parentMethod)->GetWSession()->IsValid()))
if(!(((PseuInstance*)parentMethod)->GetWSession()))
{
logerror("Invalid Script call: SCGetPlayerGuid: WorldSession not valid");
DEF_RETURN_ERROR;
@ -380,7 +380,7 @@ DefReturnResult DefScriptPackage::SCGetPlayerGuid(CmdSet& Set)
DefReturnResult DefScriptPackage::SCGetName(CmdSet& Set)
{
if(!(((PseuInstance*)parentMethod)->GetWSession() && ((PseuInstance*)parentMethod)->GetWSession()->IsValid()))
if(!(((PseuInstance*)parentMethod)->GetWSession()))
{
logerror("Invalid Script call: SCGetName: WorldSession not valid");
DEF_RETURN_ERROR;
@ -411,7 +411,7 @@ DefReturnResult DefScriptPackage::SCGetName(CmdSet& Set)
DefReturnResult DefScriptPackage::SCGetEntry(CmdSet& Set)
{
if(!(((PseuInstance*)parentMethod)->GetWSession() && ((PseuInstance*)parentMethod)->GetWSession()->IsValid()))
if(!(((PseuInstance*)parentMethod)->GetWSession()))
{
logerror("Invalid Script call: SCGetEntry: WorldSession not valid");
DEF_RETURN_ERROR;
@ -433,7 +433,7 @@ DefReturnResult DefScriptPackage::SCGetEntry(CmdSet& Set)
DefReturnResult DefScriptPackage::SCGetObjectType(CmdSet& Set)
{
if(!(((PseuInstance*)parentMethod)->GetWSession() && ((PseuInstance*)parentMethod)->GetWSession()->IsValid()))
if(!(((PseuInstance*)parentMethod)->GetWSession()))
{
logerror("Invalid Script call: SCGetObjectType: WorldSession not valid");
DEF_RETURN_ERROR;
@ -455,7 +455,7 @@ DefReturnResult DefScriptPackage::SCGetObjectType(CmdSet& Set)
DefReturnResult DefScriptPackage::SCObjectKnown(CmdSet& Set)
{
if(!(((PseuInstance*)parentMethod)->GetWSession() && ((PseuInstance*)parentMethod)->GetWSession()->IsValid()))
if(!(((PseuInstance*)parentMethod)->GetWSession()))
{
logerror("Invalid Script call: SCObjectIsKnown: WorldSession not valid");
DEF_RETURN_ERROR;
@ -489,7 +489,7 @@ DefReturnResult DefScriptPackage::SCGetScriptPerm(CmdSet& Set)
DefReturnResult DefScriptPackage::SCGetItemProtoValue(CmdSet& Set)
{
if(!(((PseuInstance*)parentMethod)->GetWSession() && ((PseuInstance*)parentMethod)->GetWSession()->IsValid()))
if(!(((PseuInstance*)parentMethod)->GetWSession()))
{
logerror("Invalid Script call: SCGetItemProtoValue: WorldSession not valid");
DEF_RETURN_ERROR;

View File

@ -8,8 +8,8 @@
#include "DefScriptInterface.h"
#include "Auth/BigNumber.h"
#include "DefScript/DefScript.h"
#include "Realm/RealmSocket.h"
#include "World/WorldSession.h"
#include "RealmSession.h"
#include "WorldSession.h"
#include "CacheHandler.h"
#include "GUI/PseuGUI.h"
@ -56,7 +56,7 @@ PseuInstance::PseuInstance(PseuInstanceRunnable *run)
_stop=false;
_fastquit=false;
_startrealm=true;
createWorldSession=false;
_createws=false;
_error=false;
_initialized=false;
@ -65,15 +65,20 @@ PseuInstance::PseuInstance(PseuInstanceRunnable *run)
PseuInstance::~PseuInstance()
{
delete _wsession;
if(GetConf()->enablecli && _cli)
if(_cli)
{
_cli->stop();
}
if(_rsession)
delete _rsession;
if(_wsession)
delete _wsession;
delete _scp;
delete _conf;
//delete _rsession; // deleted by SocketHandler!!!!!
log_close();
delete _conf;
log_close();
}
@ -181,50 +186,50 @@ bool PseuInstance::Init(void) {
void PseuInstance::Run(void)
{
do
if(!_initialized)
return;
if(GetConf()->realmlist.empty() || GetConf()->realmport==0)
{
if(!_initialized)
return;
logcritical("Realmlist address not set, can't connect.");
SetError();
}
else
{
// for now: create the realmsession only on startup.
// may be extended to a script command later on.
// then try to connect
_rsession = new RealmSession(this);
_rsession->Connect();
_rsession->SendLogonChallenge();
if(GetConf()->realmlist.empty() || GetConf()->realmport==0)
{
logcritical("Realmlist address not set, can't connect.");
SetError();
break;
}
_rsession=new RealmSocket(_sh);
_rsession->SetDeleteByHandler();
_rsession->SetHost(GetConf()->realmlist);
_rsession->SetPort(GetConf()->realmport);
_rsession->SetInstance(this);
_rsession->Start();
if(_rsession->IsValid())
{
_sh.Add(_rsession);
_sh.Select(1,0);
}
_startrealm=false; // the realm is started now
while( (!_stop) && (!_startrealm) )
// this is the mainloop
while(!_stop)
{
Update();
if(_error)
_stop=true;
}
}
while(GetConf()->reconnect && (!_stop));
}
// fastquit is defined if we clicked [X] (on windows)
if(_fastquit)
{
log("Aborting Instance...");
return;
}
log("Shutting down instance...");
SaveAllCache();
// if there was an error, better dont save, as the data might be damaged
if(!_error)
{
SaveAllCache();
//...
}
if(GetConf()->exitonerror == false && _error)
{
@ -238,45 +243,49 @@ void PseuInstance::Run(void)
void PseuInstance::Update()
{
if(_sh.GetCount())
// delete sessions if they are no longer needed
if(_rsession && _rsession->MustDie())
{
_sh.Select(0,0); // update the realmsocket
if(deleterealm)
{
deleterealm=false;
_rsession = NULL; // was deleted by SocketHandler already!
}
delete _rsession;
_rsession = NULL;
}
if(createWorldSession && (!_wsession))
if(_wsession && _wsession->MustDie())
{
createWorldSession=false;
_wsession=new WorldSession(this);
delete _wsession;
_wsession = NULL;
}
if(_wsession && !_wsession->IsValid())
if(_createws)
{
_createws = false;
if(_wsession)
delete _wsession;
_wsession = new WorldSession(this);
_wsession->Start();
}
if(_wsession && _wsession->IsValid())
// if we have no active sessions, we may reconnect
if((!_rsession) && (!_wsession) && GetConf()->reconnect)
{
try
{
_wsession->Update(); // update the worldSESSION, which will update the worldsocket itself
}
catch (...)
logdetail("Waiting %u ms before reconnecting.",GetConf()->reconnect);
for(uint32 t = 0; t < GetConf()->reconnect && !this->Stopped(); t+=100) Sleep(100);
this->Sleep(1000); // wait 1 sec before reconnecting
_rsession = new RealmSession(this);
_rsession->Connect();
_rsession->SendLogonChallenge(); // and login again
}
// update currently existing/active sessions
if(_rsession)
_rsession->Update();
if(_wsession)
try { _wsession->Update(); } catch (...)
{
logerror("Unhandled exception in WorldSession::Update()");
}
}
if(_wsession && _wsession->DeleteMe())
{
delete _wsession;
_wsession=NULL;
_startrealm=true;
this->Sleep(1000); // wait 1 sec before reconnecting
return;
}
GetScripts()->GetEventMgr()->Update();
this->Sleep(GetConf()->networksleeptime);
@ -285,7 +294,7 @@ void PseuInstance::Update()
void PseuInstance::SaveAllCache(void)
{
//...
if(GetWSession() && GetWSession()->IsValid())
if(GetWSession())
{
GetWSession()->plrNameCache.SaveToFile();
ItemProtoCache_WriteDataToCache(GetWSession());
@ -311,7 +320,7 @@ void PseuInstanceConf::ApplyFromVarSet(VarSet &v)
accname=v.Get("ACCNAME");
accpass=v.Get("ACCPASS");
exitonerror=(bool)atoi(v.Get("EXITONERROR").c_str());
reconnect=(bool)atoi(v.Get("RECONNECT").c_str());
reconnect=atoi(v.Get("RECONNECT").c_str());
realmport=atoi(v.Get("REALMPORT").c_str());
clientversion_string=v.Get("CLIENTVERSION");
clientbuild=atoi(v.Get("CLIENTBUILD").c_str());

View File

@ -10,7 +10,7 @@
#include "Network/SocketHandler.h"
#include "SCPDatabase.h"
class RealmSocket;
class RealmSession;
class WorldSession;
class Sockethandler;
class PseuInstanceRunnable;
@ -30,7 +30,7 @@ class PseuInstanceConf
std::string accname;
std::string accpass;
bool exitonerror;
bool reconnect;
uint32 reconnect;
uint16 realmport;
uint16 worldport;
uint8 clientversion[3];
@ -67,7 +67,7 @@ class PseuInstance
WorldSession *GetWSession(void) { return _wsession; }
RealmSocket *GetRSession(void) { return _rsession; }
RealmSession *GetRSession(void) { return _rsession; }
PseuInstanceConf *GetConf(void) { return _conf; }
DefScriptPackage *GetScripts(void) { return _scp; }
PseuInstanceRunnable *GetRunnable(void) { return _runnable; }
@ -82,19 +82,19 @@ class PseuInstance
bool Init();
void SaveAllCache(void);
void Stop(void) { _stop = true; }
bool Stopped(void) { return _stop; }
void SetFastQuit(bool q=true) { _fastquit=true; }
void Quit(void);
void Run(void);
void Update(void);
void Sleep(uint32 msecs);
bool createWorldSession;
bool deleterealm;
void CreateWorldSession(void) { _createws = true; }
private:
PseuInstanceRunnable *_runnable;
RealmSocket *_rsession;
RealmSession *_rsession;
WorldSession *_wsession;
PseuInstanceConf *_conf;
DefScriptPackage *_scp;
@ -103,6 +103,7 @@ class PseuInstance
bool _stop,_fastquit;
bool _startrealm;
bool _error;
bool _createws;
BigNumber _sessionkey;
char *_ver,*_ver_short;
SocketHandler _sh;

View File

@ -0,0 +1,489 @@
#include "common.h"
#include "Auth/Sha1.h"
#include "Auth/BigNumber.h"
#include "PseuWoW.h"
#include "RealmSocket.h"
#include "RealmSession.h"
enum AuthCmd
{
AUTH_LOGON_CHALLENGE = 0x00,
AUTH_LOGON_PROOF = 0x01,
REALM_LIST = 0x10,
};
struct SRealmHeader
{
uint8 cmd; // OP code = CMD_REALM_LIST
uint16 size; // size of the rest of packet, without this part
uint32 unknown; // 0x00 00 00 00
uint8 count; // quantity of realms
};
struct SRealmInfo
{
uint8 icon; // icon near realm
uint8 locked; // added in 2.0.x
uint8 color; // color of record
std::string name; // Text zero terminated name of Realm
std::string addr_port; // Text zero terminated address of Realm ("ip:port")
float population; // 1.6 -> population value. lower == lower population and vice versa
uint8 chars_here; // number of characters on this server
uint8 timezone; // timezone
uint8 unknown; //
};
struct AuthHandler
{
uint32 cmd;
void (RealmSession::*handler)(ByteBuffer&);
};
struct sAuthLogonChallenge_S
{
uint8 cmd;
uint8 error;
uint8 unk2;
uint8 B[32];
uint8 g_len;
uint8 g[1];
uint8 N_len;
uint8 N[32];
uint8 salt[32];
uint8 unk3[16];
};
struct sAuthLogonProof_S
{
uint8 cmd;
uint8 error;
uint8 M2[20];
uint32 unk2;
};
RealmSession::RealmSession(PseuInstance* instance)
{
_instance = instance;
_socket = NULL;
_mustdie = false;
_sh.SetAutoCloseSockets(false);
}
RealmSession::~RealmSession()
{
// drop the socket
ClearSocket();
// clear the queue
ByteBuffer *packet;
while(!pktQueue.empty())
{
packet = pktQueue.next();
delete packet;
}
memset(_m2,0,20);
_key=0;
}
void RealmSession::Connect(void)
{
ClearSocket();
_socket = new RealmSocket(_sh);
_socket->SetSession(this);
_socket->Open(GetInstance()->GetConf()->realmlist,GetInstance()->GetConf()->realmport);
_sh.Add(_socket);
_sh.Select(3,0);
}
void RealmSession::ClearSocket(void)
{
if(_socket)
{
delete _socket;
_socket = NULL;
}
}
void RealmSession::SetMustDie(void)
{
_mustdie = true;
}
bool RealmSession::MustDie(void)
{
return _mustdie;
}
AuthHandler *RealmSession::_GetAuthHandlerTable(void) const
{
static AuthHandler table[] =
{
{AUTH_LOGON_CHALLENGE,&RealmSession::_HandleLogonChallenge},
{AUTH_LOGON_PROOF,&RealmSession::_HandleLogonProof},
{REALM_LIST,&RealmSession::_HandleRealmList},
{0,NULL}
};
return table;
}
void RealmSession::AddToPktQueue(ByteBuffer *pkt)
{
pktQueue.add(pkt);
}
void RealmSession::Update(void)
{
AuthHandler *table = _GetAuthHandlerTable();
ByteBuffer *pkt;
uint8 cmd;
if( _sh.GetCount() )
{
_sh.Select(0,0);
}
while(pktQueue.size())
{
pkt = pktQueue.next();
cmd = (*pkt)[0];
for(uint8 i=0;table[i].handler!=NULL;i++)
{
if(table[i].cmd==cmd)
{
(this->*table[i].handler)(*pkt);
if(pkt->rpos() < pkt->size())
{
uint32 len = pkt->size() - pkt->rpos();
uint8 *data = new uint8[len];
pkt->read(data,len); // if we have data crap left on the buf, delete it
logdebug("Data left on RealmSocket, Hexdump:");
logdebug(toHexDump(data,len).c_str());
delete [] data;
}
delete pkt;
}
}
}
}
PseuInstance *RealmSession::GetInstance(void)
{
return _instance;
}
void RealmSession::_HandleRealmList(ByteBuffer& pkt)
{
std::string realmAddr;
uint32 unk;
uint16 len,count;
uint8 cmd;
pkt >> cmd >> len >> unk >> count;
// no realm?
if(count==0)
return;
// alloc space for as many realms as needed
SRealmInfo *realms=new SRealmInfo[count];
// readout realms
for(uint8 i=0;i<count;i++)
{
pkt >> realms[i].icon;
pkt >> realms[i].locked;
pkt >> realms[i].color;
pkt >> realms[i].name;
pkt >> realms[i].addr_port;
pkt >> realms[i].population;
pkt >> realms[i].chars_here;
pkt >> realms[i].timezone;
pkt >> realms[i].unknown;
}
// the rest of the packet is not interesting
for(uint8 i=0;i<count;i++)
{
if(realms[i].name==GetInstance()->GetConf()->realmname)
{
realmAddr=realms[i].addr_port;
}
logcustom(0,LGREEN,"Realm: %s (%s)",realms[i].name.c_str(),realms[i].addr_port.c_str());
logdetail(" [chars:%d][population:%f][timezone:%d]",realms[i].chars_here,realms[i].population,realms[i].timezone);
}
delete [] realms;
// now setup where the worldserver is and how to login there
if(realmAddr.empty()){
log("Realm \"%s\" was not found on the realmlist!",GetInstance()->GetConf()->realmname.c_str());
return;
}
// transform "hostname:port" into something useful
// -> convert the worldserver port from string to int
// -> write it into the config & set appropriate vars
uint16 colonpos=realmAddr.find(":");
GetInstance()->GetConf()->worldhost=realmAddr.substr(0,colonpos);
GetInstance()->GetConf()->worldport=atoi(realmAddr.substr(colonpos+1,realmAddr.length()-colonpos-1).c_str());
// set vars
GetInstance()->GetScripts()->variables.Set("WORLDHOST",GetInstance()->GetConf()->worldhost);
GetInstance()->GetScripts()->variables.Set("WORLDPORT",toString((uint64)(GetInstance()->GetConf()->worldport)));
// now we have the correct addr/port, time to create the WorldSession
GetInstance()->CreateWorldSession(); // will be done at next PseuInstance::Update()
}
void RealmSession::SendLogonChallenge(void)
{
if( GetInstance()->GetConf()->accname.empty() || GetInstance()->GetConf()->clientversion_string.empty()
|| GetInstance()->GetConf()->clientbuild==0 || GetInstance()->GetConf()->clientlang.empty() )
{
logcritical("Missing data, can't send Login to Realm Server!");
GetInstance()->SetError();
return;
}
std::string acc = stringToUpper(GetInstance()->GetConf()->accname);
ByteBuffer packet;
packet << (uint8)AUTH_LOGON_CHALLENGE;
packet << (uint8)2;
packet << (uint8)(acc.length()+30); // length of the rest of the packet
packet << (uint8)0;
packet.append("WOW",3);
packet.append(GetInstance()->GetConf()->clientversion,3); // 1.12.2 etc
packet << (uint8)0;
packet << (uint16)(GetInstance()->GetConf()->clientbuild); // (uint16) 5875
packet << "68x" << "niW"; // "x86" - platform; "Win" - Operating system; both reversed and zero terminated
for(uint8 i=0;i<4;i++)
packet << (uint8)(GetInstance()->GetConf()->clientlang[3-i]); // "enUS" -> "SUne" : reversed and NOT zero terminated
packet << (uint32)0x3c; // timezone
packet << (uint32)_socket->GetClientRemoteAddr(); // my IP address
packet << (uint8)acc.length(); // length of acc name without \0
packet.append(acc.c_str(),acc.length()); // append accname, skip \0
SendRealmPacket(packet);
}
void RealmSession::_HandleLogonChallenge(ByteBuffer& pkt)
{
logdebug("RealmSocket: Got AUTH_LOGON_CHALLENGE [%u of %u bytes]",pkt.size(),sizeof(sAuthLogonChallenge_S));
if(pkt.size() < sizeof(sAuthLogonChallenge_S))
{
logerror("AUTH_LOGON_CHALLENGE: Recieved incorrect/unknown packet. Hexdump:");
DumpInvalidPacket(pkt);
return;
}
sAuthLogonChallenge_S lc;
pkt.read((uint8*)&lc, sizeof(sAuthLogonChallenge_S));
switch (lc.error)
{
case 4:
log("Realm Server did not find account \"%s\"!",GetInstance()->GetConf()->accname.c_str());
break;
case 6:
log("Account \"%s\" is already logged in!",GetInstance()->GetConf()->accname.c_str());
// TODO: wait a certain amount of time before reconnecting? conf option?
break;
case 9:
log("Realm Server doesn't accept this version!");
break;
case 0:
{
logdetail("Login successful, now calculating proof packet...");
// now lets start calculating
BigNumber N,A,B,a,u,x,v,S,salt,unk1,g,k(3); // init BNs, default k to 3
std::string user=stringToUpper( GetInstance()->GetConf()->accname );
std::string _authstr=stringToUpper( user +":"+GetInstance()->GetConf()->accpass );
B.SetBinary(lc.B,32);
g.SetBinary(lc.g,lc.g_len);
N.SetBinary(lc.N,lc.N_len);
salt.SetBinary(lc.salt,32);
unk1.SetBinary(lc.unk3,16);
logdebug("== Server Bignums ==");
logdebug("--> B=%s",B.AsHexStr());
logdebug("--> g=%s",g.AsHexStr());
logdebug("--> N=%s",N.AsHexStr());
logdebug("--> salt=%s",salt.AsHexStr());
logdebug("--> unk=%s",unk1.AsHexStr());
logdebug("== My Bignums ==");
a.SetRand(19*8);
ASSERT(a.AsDword() > 0);
logdebug("--> a=%s",a.AsHexStr());
Sha1Hash userhash,xhash,uhash;
userhash.UpdateData(_authstr);
userhash.Finalize();
xhash.UpdateData(salt.AsByteArray(),salt.GetNumBytes());
xhash.UpdateData(userhash.GetDigest(),userhash.GetLength());
xhash.Finalize();
x.SetBinary(xhash.GetDigest(),xhash.GetLength());
logdebug("--> x=%s",x.AsHexStr());
v=g.ModExp(x,N);
logdebug("--> v=%s",v.AsHexStr());
A=g.ModExp(a,N);
logdebug("--> A=%s",A.AsHexStr());
uhash.UpdateBigNumbers(&A, &B, NULL);
uhash.Finalize();
u.SetBinary(uhash.GetDigest(), 20);
logdebug("--> u=%s",u.AsHexStr());
S=(B - k*g.ModExp(x,N) ).ModExp((a + u * x),N);
logdebug("--> S=%s",S.AsHexStr());
ASSERT(S.AsDword() > 0);
// calc M1 & M2
unsigned int i=0;
char S1[16+1],S2[16+1]; // 32/2=16 :) +1 for \0
// split it into 2 seperate strings, interleaved
for(i=0;i<16;i++){
S1[i]=S.AsByteArray()[i*2];
S2[i]=S.AsByteArray()[i*2+1];
}
// hash each one:
Sha1Hash S1hash,S2hash;
S1hash.UpdateData((const uint8*)S1,16);
S1hash.Finalize();
S2hash.UpdateData((const uint8*)S2,16);
S2hash.Finalize();
// Re-combine them
char S_hash[40];
for(i=0;i<20;i++){
S_hash[i*2]=S1hash.GetDigest()[i];
S_hash[i*2+1]=S2hash.GetDigest()[i];
}
_key.SetBinary((uint8*)S_hash,40); // used later when authing to world
logdebug("--> SessionKey=%s",_key.AsHexStr());
char Ng_hash[20];
Sha1Hash userhash2,Nhash,ghash;
userhash2.UpdateData((const uint8*)user.c_str(),user.length());
userhash2.Finalize();
//printchex((char*)userhash2.GetDigest(),userhash2.GetLength(),true);
Nhash.UpdateBigNumbers(&N,NULL);
Nhash.Finalize();
ghash.UpdateBigNumbers(&g,NULL);
ghash.Finalize();
for(i=0;i<20;i++)Ng_hash[i] = Nhash.GetDigest()[i]^ghash.GetDigest()[i];
//printchex(Ng_hash,20,true);
BigNumber t_acc,t_Ng_hash;
t_acc.SetBinary((const uint8*)userhash2.GetDigest(),userhash2.GetLength());
t_Ng_hash.SetBinary((const uint8*)Ng_hash,20);
Sha1Hash M1hash,M2hash;
M1hash.UpdateBigNumbers(&t_Ng_hash,&t_acc,&salt,&A,&B,NULL);
M1hash.UpdateData((const uint8*)S_hash,40);
M1hash.Finalize();
M2hash.UpdateBigNumbers(&A,NULL);
M2hash.UpdateData((const uint8*)M1hash.GetDigest(),M1hash.GetLength());
M2hash.UpdateData((const uint8*)S_hash,40);
M2hash.Finalize();
logdebug("== Common Hashes ==");
logdebug("--> M1=%s",toHexDump(M1hash.GetDigest(),M1hash.GetLength(),false).c_str());
logdebug("--> M2=%s",toHexDump(M2hash.GetDigest(),M2hash.GetLength(),false).c_str());
// Calc CRC & CRC_hash
// i don't know yet how to calc it, so set it to zero
char crc_hash[20];
memset(crc_hash,0,20);
logdebug("--> CRC=%s",toHexDump((uint8*)crc_hash,20,false).c_str());
// now lets prepare the packet
ByteBuffer packet;
packet << (uint8)AUTH_LOGON_PROOF;
packet.append(A.AsByteArray(),A.GetNumBytes());
packet.append(M1hash.GetDigest(),M1hash.GetLength());
packet.append(crc_hash,20);
packet << (uint8)0; // number of keys = 0
if(GetInstance()->GetConf()->clientbuild > 5302)
packet << (uint8)0; // 1.11.x compatibility (needs one more 0)
GetInstance()->SetSessionKey(_key);
memcpy(this->_m2,M2hash.GetDigest(),M2hash.GetLength()); // save M2 to an extern var to check it later
SendRealmPacket(packet);
}
break;
default:
logerror("Unknown realm server response! opcode=0x%x\n",(unsigned char)lc.error);
DumpInvalidPacket(pkt);
break;
}
}
void RealmSession::_HandleLogonProof(ByteBuffer& pkt)
{
logdebug("RealmSocket: Got AUTH_LOGON_PROOF [%u of %u bytes]\n",pkt.size(),26);
if(pkt.size() < 26)
{
logerror("AUTH_LOGON_PROOF: Recieved incorrect/unknown packet. Hexdump:");
DumpInvalidPacket(pkt);
if(GetInstance()->GetConf()->reconnect)
SetMustDie();
return;
}
sAuthLogonProof_S lp;
pkt.read((uint8*)&lp, 26); // the compiler didnt like 'sizeof(sAuthLogonProof_S)', said it was 28
//printchex((char*)&lp, sizeof(sAuthLogonProof_S),true);
if(!memcmp(lp.M2,this->_m2,20))
{
// auth successful
ByteBuffer packet;
packet << (uint8)REALM_LIST;
packet << (uint32)0;
SendRealmPacket(packet);
}
else
{
logcritical("Auth failed, M2 differs!");
printf("My M2 :"); printchex((char*)_m2,20,true);
printf("Srv M2:"); printchex((char*)lp.M2,20,true);
if(GetInstance()->GetConf()->reconnect)
SetMustDie();
else
GetInstance()->SetError();
}
}
void RealmSession::DumpInvalidPacket(ByteBuffer& pkt)
{
if(pkt.size())
logerror( toHexDump((uint8*)pkt.contents(),pkt.size()).c_str() );
}
void RealmSession::SendRealmPacket(ByteBuffer& pkt)
{
if(_socket && _socket->IsOk())
{
if(pkt.size()) // dont send packets with no data
_socket->SendBuf((const char*)pkt.contents(),pkt.size());
}
else
{
logerror("Can't send realm packet, socket does not exist or is not ready!");
}
}

View File

@ -0,0 +1,45 @@
#ifndef REALMSESSION_H
#define REALMSESSION_H
#include "common.h"
struct AuthHandler;
class RealmSocket;
class RealmSession
{
public:
RealmSession(PseuInstance*);
~RealmSession();
void AddToPktQueue(ByteBuffer*);
void Connect(void);
void Update(void);
PseuInstance *GetInstance(void);
void ClearSocket(void);
void SendLogonChallenge(void);
bool MustDie(void);
void SetMustDie(void);
private:
void _HandleRealmList(ByteBuffer&);
void _HandleLogonProof(ByteBuffer&);
void _HandleLogonChallenge(ByteBuffer&);
AuthHandler *_GetAuthHandlerTable(void) const;
void SendRealmPacket(ByteBuffer&);
void DumpInvalidPacket(ByteBuffer&);
SocketHandler _sh;
PseuInstance *_instance;
ZThread::LockedQueue<ByteBuffer*,ZThread::FastMutex> pktQueue;
RealmSocket *_socket;
uint8 _m2[20];
RealmSession *_session;
BigNumber _key;
bool _mustdie;
};
#endif

View File

@ -1,473 +1,82 @@
#include "common.h"
#include "PseuWoW.h"
#include "ByteBuffer.h"
#include "Auth/Sha1.h"
#include "Auth/BigNumber.h"
#include "RealmSession.h"
#include "RealmSocket.h"
#include "WorldSession.h"
enum AuthCmd
{
AUTH_LOGON_CHALLENGE = 0x00,
AUTH_LOGON_PROOF = 0x01,
REALM_LIST = 0x10,
};
struct SRealmHeader
{
uint8 cmd; // OP code = CMD_REALM_LIST
uint16 size; // size of the rest of packet, without this part
uint32 unknown; // 0x00 00 00 00
uint8 count; // quantity of realms
};
struct SRealmInfo
{
uint8 icon; // icon near realm
uint8 locked; // added in 2.0.x
uint8 color; // color of record
std::string name; // Text zero terminated name of Realm
std::string addr_port; // Text zero terminated address of Realm ("ip:port")
float population; // 1.6 -> population value. lower == lower population and vice versa
uint8 chars_here; // number of characters on this server
uint8 timezone; // timezone
uint8 unknown; //
};
struct AuthHandler
{
uint32 cmd;
void (RealmSocket::*handler)(void);
};
struct sAuthLogonChallenge_S
{
uint8 cmd;
uint8 error;
uint8 unk2;
uint8 B[32];
uint8 g_len;
uint8 g[1];
uint8 N_len;
uint8 N[32];
uint8 salt[32];
uint8 unk3[16];
};
struct sAuthLogonProof_S
{
uint8 cmd;
uint8 error;
uint8 M2[20];
uint32 unk2;
};
const AuthHandler table[]=
{
{AUTH_LOGON_CHALLENGE,&RealmSocket::_HandleLogonChallenge},
{AUTH_LOGON_PROOF,&RealmSocket::_HandleLogonProof},
{REALM_LIST,&RealmSocket::_HandleRealmList},
{0,NULL}
};
RealmSocket::RealmSocket(SocketHandler& h) : TcpSocket(h)
{
_instance=NULL;
_valid=false;
_rport=3724;
_session = NULL;
_ok = false;
}
RealmSocket::~RealmSocket()
{
Stop();
}
bool RealmSocket::IsValid(void)
{
return _valid;
}
void RealmSocket::SetHost(std::string h)
{
_rhost=h;
}
void RealmSocket::SetPort(uint16 p)
{
_rport=p;
}
void RealmSocket::SetInstance(PseuInstance *pi)
{
_instance=pi;
}
void RealmSocket::Start(void)
{
if(_rhost.empty() || _rport==0 || _instance==NULL)
return;
log("Connecting to Realm Server on '%s:%u'",_rhost.c_str(),_rport);
Open(_rhost,_rport);
//...
_valid=true;
}
void RealmSocket::Stop(void)
{
_valid=false;
this->Close();
memset(_m2,0,20);
_key=0;
}
void RealmSocket::_HandleRealmList(void)
bool RealmSocket::IsOk(void)
{
std::string realmAddr;
ByteBuffer realmbuf;
realmbuf.resize(ibuf.GetLength());
ibuf.Read((char*)realmbuf.contents(), ibuf.GetLength());
return _ok;
}
uint32 unk;
uint16 len,count;
uint8 cmd;
realmbuf >> cmd >> len >> unk >> count;
// no realm?
if(count==0)
return;
void RealmSocket::SetSession(RealmSession *rs)
{
_session = rs;
}
// alloc space for as many realms as needed
SRealmInfo *realms=new SRealmInfo[count];
// readout realms
for(uint8 i=0;i<count;i++)
{
realmbuf >> realms[i].icon;
realmbuf >> realms[i].locked;
realmbuf >> realms[i].color;
realmbuf >> realms[i].name;
realmbuf >> realms[i].addr_port;
realmbuf >> realms[i].population;
realmbuf >> realms[i].chars_here;
realmbuf >> realms[i].timezone;
realmbuf >> realms[i].unknown;
}
// the rest of the packet is not interesting
for(uint8 i=0;i<count;i++)
{
if(realms[i].name==GetInstance()->GetConf()->realmname)
{
realmAddr=realms[i].addr_port;
}
logcustom(0,LGREEN,"Realm: %s (%s)",realms[i].name.c_str(),realms[i].addr_port.c_str());
logdetail(" [chars:%d][population:%f][timezone:%d]",realms[i].chars_here,realms[i].population,realms[i].timezone);
}
delete [] realms;
// now setup where the woldserver is and how to login there
if(realmAddr.empty()){
log("Realm \"%s\" was not found on the realmlist!",GetInstance()->GetConf()->realmname.c_str());
return;
}
// transform "hostname:port" into something useful
// -> convert the worldserver port from string to int
// -> write it into the config & set appropriate vars
uint16 colonpos=realmAddr.find(":");
GetInstance()->GetConf()->worldhost=realmAddr.substr(0,colonpos);
GetInstance()->GetConf()->worldport=atoi(realmAddr.substr(colonpos+1,realmAddr.length()-colonpos-1).c_str());
// set vars
GetInstance()->GetScripts()->variables.Set("WORLDHOST",GetInstance()->GetConf()->worldhost);
GetInstance()->GetScripts()->variables.Set("WORLDPORT",toString((uint64)(GetInstance()->GetConf()->worldport)));
// now we have the correct addr/port, time to create the WorldSession
GetInstance()->createWorldSession=true;
int RealmSocket::Close(void)
{
_ok = false;
return TcpSocket::Close();
}
void RealmSocket::OnRead(void)
{
TcpSocket::OnRead();
bool known=false;
//printf("RealmSocket::OnRead() %u bytes\n",ibuf.GetLength());
if(!ibuf.GetLength())
uint32 len = ibuf.GetLength();
logdev("RealmSocket::OnRead() %u bytes",len);
if(!len)
return;
uint8 cmd, i=0;
ibuf.SoftRead((char*)&cmd, 1);
for(uint8 i=0;table[i].handler!=NULL;i++)
{
if(table[i].cmd==cmd)
{
(*this.*table[i].handler)();
known=true;
break;
}
}
if(!known)
{
log("RealmSocket: Got unknown packet, cmd=%u",cmd);
}
if(ibuf.GetLength())
{
uint32 len = ibuf.GetLength();
char *data = new char[len];
ibuf.Read(data,len); // if we have data crap left on the buf, delete it
logdebug("Data left on RealmSocket, Hexdump:");
logdebug(toHexDump((uint8*)data,len).c_str());
delete [] data;
}
ByteBuffer *pkt = new ByteBuffer(len);
uint8* data = new uint8[len];
ibuf.Read((char*)data,len);
pkt->append(data,len);
delete [] data;
_session->AddToPktQueue(pkt);
}
void RealmSocket::SendLogonChallenge(void)
RealmSession *RealmSocket::GetSession(void)
{
if(!this->Ready())
{
logerror("Error sending AUTH_LOGON_CHALLENGE, port is not ready!\n");
return;
}
if( GetInstance()->GetConf()->accname.empty() || GetInstance()->GetConf()->clientversion_string.empty()
|| GetInstance()->GetConf()->clientbuild==0 || GetInstance()->GetConf()->clientlang.empty() )
{
logcritical("Missing data, can't send Login to Realm Server!");
GetInstance()->SetError();
return;
}
std::string acc = stringToUpper(GetInstance()->GetConf()->accname);
ByteBuffer packet;
packet << (uint8)AUTH_LOGON_CHALLENGE;
packet << (uint8)2;
packet << (uint8)(acc.length()+30); // length of the rest of the packet
packet << (uint8)0;
packet.append("WOW",3); // game name = World Of Warcraft
packet.append(GetInstance()->GetConf()->clientversion,3); // 1.12.2 etc
packet << (uint8)0;
packet << (uint16)(GetInstance()->GetConf()->clientbuild); // (uint16) 5875
packet << "68x" << "niW"; // "x86" - platform; "Win" - Operating system; both reversed and zero terminated
for(uint8 i=0;i<4;i++)
packet << (uint8)(GetInstance()->GetConf()->clientlang[3-i]); // "enUS" -> "SUne" : reversed and NOT zero terminated
packet << (uint32)0x3c; // timezone
packet << (uint32)GetClientRemoteAddr(); // my IP address
packet << (uint8)acc.length(); // length of acc name without \0
packet.append(acc.c_str(),acc.length()); // append accname, skip \0
SendBuf((char*)packet.contents(),packet.size());
return _session;
}
PseuInstance *RealmSocket::GetInstance(void)
void RealmSocket::OnAccept(void)
{
return _instance;
logdev("RealmSocket accepted.");
}
void RealmSocket::_HandleLogonChallenge(void)
{
logdebug("RealmSocket: Got AUTH_LOGON_CHALLENGE [%u of %u bytes]",ibuf.GetLength(),sizeof(sAuthLogonChallenge_S));
if(ibuf.GetLength() < sizeof(sAuthLogonChallenge_S))
{
logerror("AUTH_LOGON_CHALLENGE: Recieved incorrect/unknown packet. Hexdump:");
uint32 len = ibuf.GetLength();
char* data = new char[len];
ibuf.Read(data,len);
logerror(toHexDump((uint8*)data,len).c_str());
delete [] data;
return;
}
sAuthLogonChallenge_S lc;
ibuf.Read((char*)&lc, sizeof(sAuthLogonChallenge_S));
switch (lc.error)
{
case 4:
log("Realm Server did not find account \"%s\"!",GetInstance()->GetConf()->accname.c_str());
break;
case 6:
log("Account \"%s\" is already logged in!",GetInstance()->GetConf()->accname.c_str());
break;
case 0:
{
logdetail("Login successful, now calculating proof packet...");
// now lets start calculating
BigNumber N,A,B,a,u,x,v,S,salt,unk1,g,k(3); // init BNs, default k to 3
std::string user=stringToUpper( GetInstance()->GetConf()->accname );
std::string _authstr=stringToUpper( user +":"+GetInstance()->GetConf()->accpass );
B.SetBinary(lc.B,32);
g.SetBinary(lc.g,lc.g_len);
N.SetBinary(lc.N,lc.N_len);
salt.SetBinary(lc.salt,32);
unk1.SetBinary(lc.unk3,16);
logdebug("== Server Bignums ==");
logdebug("--> B=%s",B.AsHexStr());
logdebug("--> g=%s",g.AsHexStr());
logdebug("--> N=%s",N.AsHexStr());
logdebug("--> salt=%s",salt.AsHexStr());
logdebug("--> unk=%s",unk1.AsHexStr());
logdebug("== My Bignums ==");
a.SetRand(19*8);
ASSERT(a.AsDword() > 0);
logdebug("--> a=%s",a.AsHexStr());
Sha1Hash userhash,xhash,uhash;
userhash.UpdateData(_authstr);
userhash.Finalize();
xhash.UpdateData(salt.AsByteArray(),salt.GetNumBytes());
xhash.UpdateData(userhash.GetDigest(),userhash.GetLength());
xhash.Finalize();
x.SetBinary(xhash.GetDigest(),xhash.GetLength());
logdebug("--> x=%s",x.AsHexStr());
v=g.ModExp(x,N);
logdebug("--> v=%s",v.AsHexStr());
A=g.ModExp(a,N);
logdebug("--> A=%s",A.AsHexStr());
uhash.UpdateBigNumbers(&A, &B, NULL);
uhash.Finalize();
u.SetBinary(uhash.GetDigest(), 20);
logdebug("--> u=%s",u.AsHexStr());
S=(B - k*g.ModExp(x,N) ).ModExp((a + u * x),N);
logdebug("--> S=%s",S.AsHexStr());
ASSERT(S.AsDword() > 0);
// calc M1 & M2
unsigned int i=0;
char S1[16+1],S2[16+1]; // 32/2=16 :) +1 for \0
// split it into 2 seperate strings, interleaved
for(i=0;i<16;i++){
S1[i]=S.AsByteArray()[i*2];
S2[i]=S.AsByteArray()[i*2+1];
}
// hash each one:
Sha1Hash S1hash,S2hash;
S1hash.UpdateData((const uint8*)S1,16);
S1hash.Finalize();
S2hash.UpdateData((const uint8*)S2,16);
S2hash.Finalize();
// Re-combine them
char S_hash[40];
for(i=0;i<20;i++){
S_hash[i*2]=S1hash.GetDigest()[i];
S_hash[i*2+1]=S2hash.GetDigest()[i];
}
_key.SetBinary((uint8*)S_hash,40); // used later when authing to world
logdebug("--> SessionKey=%s",_key.AsHexStr());
char Ng_hash[20];
Sha1Hash userhash2,Nhash,ghash;
userhash2.UpdateData((const uint8*)user.c_str(),user.length());
userhash2.Finalize();
//printchex((char*)userhash2.GetDigest(),userhash2.GetLength(),true);
Nhash.UpdateBigNumbers(&N,NULL);
Nhash.Finalize();
ghash.UpdateBigNumbers(&g,NULL);
ghash.Finalize();
for(i=0;i<20;i++)Ng_hash[i] = Nhash.GetDigest()[i]^ghash.GetDigest()[i];
//printchex(Ng_hash,20,true);
BigNumber t_acc,t_Ng_hash;
t_acc.SetBinary((const uint8*)userhash2.GetDigest(),userhash2.GetLength());
t_Ng_hash.SetBinary((const uint8*)Ng_hash,20);
Sha1Hash M1hash,M2hash;
M1hash.UpdateBigNumbers(&t_Ng_hash,&t_acc,&salt,&A,&B,NULL);
M1hash.UpdateData((const uint8*)S_hash,40);
M1hash.Finalize();
M2hash.UpdateBigNumbers(&A,NULL);
M2hash.UpdateData((const uint8*)M1hash.GetDigest(),M1hash.GetLength());
M2hash.UpdateData((const uint8*)S_hash,40);
M2hash.Finalize();
logdebug("== Common Hashes ==");
logdebug("--> M1=%s",toHexDump(M1hash.GetDigest(),M1hash.GetLength(),false).c_str());
logdebug("--> M2=%s",toHexDump(M2hash.GetDigest(),M2hash.GetLength(),false).c_str());
// Calc CRC & CRC_hash
// i don't know yet how to calc it, so set it to zero
char crc_hash[20];
memset(crc_hash,0,20);
logdebug("--> CRC=%s",toHexDump((uint8*)crc_hash,20,false).c_str());
// now lets prepare the packet
ByteBuffer packet;
packet << (uint8)AUTH_LOGON_PROOF;
packet.append(A.AsByteArray(),A.GetNumBytes());
packet.append(M1hash.GetDigest(),M1hash.GetLength());
packet.append(crc_hash,20);
packet << (uint8)0; // number of keys = 0
if(GetInstance()->GetConf()->clientbuild > 5302)
packet << (uint8)0; // 1.11.x compatibility (needs one more 0)
GetInstance()->SetSessionKey(_key);
memcpy(this->_m2,M2hash.GetDigest(),M2hash.GetLength()); // save M2 to an extern var to check it later
SendBuf((char*)packet.contents(),packet.size());
}
break;
default:
logerror("Unknown realm server response! opcode=0x%x\n",(unsigned char)lc.error);
break;
}
}
void RealmSocket::_HandleLogonProof(void)
{
logdebug("RealmSocket: Got AUTH_LOGON_PROOF [%u of %u bytes]\n",ibuf.GetLength(),26);
if(ibuf.GetLength() < 26)
{
logerror("AUTH_LOGON_PROOF: Recieved incorrect/unknown packet. Hexdump:");
uint32 len = ibuf.GetLength();
char* data = new char[len];
ibuf.Read(data,len);
logerror(toHexDump((uint8*)data,len).c_str());
delete [] data;
return;
}
sAuthLogonProof_S lp;
ibuf.Read((char*)&lp, 26); // the compiler didnt like 'sizeof(sAuthLogonProof_S)', said it was 28
//printchex((char*)&lp, sizeof(sAuthLogonProof_S),true);
if(!memcmp(lp.M2,this->_m2,20))
{
// auth successful
ByteBuffer packet;
packet << (uint8)REALM_LIST;
packet << (uint32)0;
SendBuf((char*)packet.contents(),packet.size());
}
else
{
logcritical("Auth failed, M2 differs!");
printf("My M2 :"); printchex((char*)_m2,20,true);
printf("Srv M2:"); printchex((char*)lp.M2,20,true);
GetInstance()->SetError();
}
}
void RealmSocket::OnConnect()
void RealmSocket::OnConnect(void)
{
logdetail("RealmSocket connected!");
SendLogonChallenge();
_ok = true;
}
void RealmSocket::OnConnectFailed(void)
{
logerror("Connecting to Realm failed!");
_ok = false;
}
void RealmSocket::OnException(void)
{
logerror("RealmSocket: Exception!");
_ok = false;
}

View File

@ -1,52 +1,34 @@
#ifndef _REALMSOCKET_H
#define _REALMSOCKET_H
#include "Network/ResolvSocket.h"
#include "PseuWoW.h"
#include "Network/TcpSocket.h"
class RealmSession;
class RealmSocket : public TcpSocket
{
public:
RealmSocket(SocketHandler &h);
~RealmSocket();
PseuInstance *GetInstance(void);
bool IsValid(void);
void SetInstance(PseuInstance*);
void SetHost(std::string);
void SetPort(uint16);
RealmSession *GetSession(void);
bool IsOk(void);
void SetSession(RealmSession*);
void Start(void);
void Stop(void);
void Update(void);
void SendLogonChallenge(void);
// dont use from outside!!
void _HandleRealmList(void);
void _HandleLogonProof(void);
void _HandleLogonChallenge(void);
void OnRead(void);
void OnConnect(void);
void OnConnectFailed(void);
void OnException(void);
void OnAccept(void);
int Close(void);
private:
uint16 _rport;
std::string _rhost;
uint8 _m2[20];
PseuInstance *_instance;
BigNumber _key;
bool _valid;
ByteBuffer _data;
bool _ok;
RealmSession *_session;
};

View File

@ -7,7 +7,7 @@
#include "Channel.h"
void WorldSession::SendChatMessage(uint32 type, uint32 lang, std::string msg, std::string to){
if((!_valid) || (!_logged) || msg.empty())
if((!_logged) || msg.empty())
return;
WorldPacket packet;
packet<<type<<lang;

View File

@ -8,21 +8,26 @@
#include "WorldSocket.h"
#include "RealmSocket.h"
#include "Channel.h"
#include "RealmSession.h"
#include "WorldSession.h"
struct OpcodeHandler
{
uint16 opcode;
void (WorldSession::*handler)(WorldPacket& recvPacket);
};
WorldSession::WorldSession(PseuInstance *in)
{
logdebug("-> Starting WorldSession from instance 0x%X",in); // should never output a null ptr
_instance = in;
_valid=_authed=_logged=false;
_socket=new WorldSocket(_sh,this);
_mustdie=false;
_logged=false;
_socket=NULL;
_myGUID=0; // i dont have a guid yet
plrNameCache.ReadFromFile(); // load names/guids of known players
ItemProtoCache_InsertDataToSession(this);
_deleteme = false;
_channels = new Channel(this);
_sh.SetAutoCloseSockets(false);
//...
}
@ -30,40 +35,43 @@ WorldSession::~WorldSession()
{
WorldPacket *packet;
// clear the queue
while(!pktQueue.empty())
while(pktQueue.size())
{
packet = pktQueue.next();
delete packet;
}
_OnLeaveWorld();
delete _channels;
//delete _socket; the socket will be deleted by its handler!!
if(_channels)
delete _channels;
if(_socket)
delete _socket;
}
void WorldSession::Start(void)
{
log("Connecting to '%s' on port %u",GetInstance()->GetConf()->worldhost.c_str(),GetInstance()->GetConf()->worldport);
_socket=new WorldSocket(_sh,this);
_socket->Open(GetInstance()->GetConf()->worldhost,GetInstance()->GetConf()->worldport);
if(GetInstance()->GetRSession())
{
GetInstance()->GetRSession()->SetCloseAndDelete(); // realm socket is no longer needed
GetInstance()->deleterealm=true;
GetInstance()->GetRSession()->SetMustDie(); // realm session is no longer needed
}
_valid=true;
_sh.Add(_socket);
_socket->SetDeleteByHandler();
_sh.Select(1,0);
// if we cant connect, wait until the socket gives up (after 5 secs)
while( (!MustDie()) && (!_socket->IsOk()) && (!GetInstance()->Stopped()) )
{
_sh.Select(3,0);
GetInstance()->Sleep(100);
}
}
bool WorldSession::DeleteMe(void)
void WorldSession::_LoadCache(void)
{
return _deleteme;
}
void WorldSession::SetSocket(WorldSocket *sock)
{
_socket = sock;
logdetail("Loading Cache...");
plrNameCache.ReadFromFile(); // load names/guids of known players
ItemProtoCache_InsertDataToSession(this);
//...
}
void WorldSession::AddToPktQueue(WorldPacket *pkt)
@ -75,37 +83,25 @@ void WorldSession::SendWorldPacket(WorldPacket &pkt)
{
if(GetInstance()->GetConf()->showmyopcodes)
logcustom(0,BROWN,"<< Opcode %u [%s]", pkt.GetOpcode(), GetOpcodeName(pkt.GetOpcode()));
_socket->SendWorldPacket(pkt);
if(_socket && _socket->IsOk())
_socket->SendWorldPacket(pkt);
else
{
logerror("WorldSession: Can't send WorldPacket, socket doesn't exist or is not ready.");
}
}
void WorldSession::Update(void)
{
if (!IsValid())
return;
if( _socket && _sh.GetCount() )
if( _sh.GetCount() ) // the socketwil remove itself from the handler if it got closed
_sh.Select(0,0);
/*if(!_socket)
{
if(_valid)
{
_deleteme = true;
}
_logged=_authed=_valid=false;
return;
}*/
if(!_socket)
{
if(_valid)
{
this->Start();
}
_logged=_authed=_valid=false;
return;
else // so we just need to check if the socket doesnt exist or if it exists but isnt valid anymore.
{ // if thats the case, we dont need the session anymore either
if(!_socket || (_socket && !_socket->IsOk()))
SetMustDie();
}
OpcodeHandler *table = _GetOpcodeHandlerTable();
bool known=false;
while(pktQueue.size())
@ -212,7 +208,7 @@ void WorldSession::SetTarget(uint64 guid)
void WorldSession::_OnEnterWorld(void)
{
if(!_logged)
if(!InWorld())
{
_logged=true;
GetInstance()->GetScripts()->variables.Set("@inworld","true");
@ -223,7 +219,7 @@ void WorldSession::_OnEnterWorld(void)
void WorldSession::_OnLeaveWorld(void)
{
if(_logged)
if(InWorld())
{
_logged=false;
GetInstance()->GetScripts()->RunScript("_leaveworld",NULL);
@ -234,7 +230,7 @@ void WorldSession::_OnLeaveWorld(void)
void WorldSession::_DoTimedActions(void)
{
static clock_t pingtime=0;
if(_logged)
if(InWorld())
{
if(pingtime < clock())
{
@ -283,8 +279,6 @@ void WorldSession::_HandleAuthChallengeOpcode(WorldPacket& recvPacket)
// so its not 100% correct to init the crypt here, but it should do the job if authing was correct
_socket->InitCrypt(GetInstance()->GetSessionKey().AsByteArray(), 40);
_authed=true;
}
void WorldSession::_HandleAuthResponseOpcode(WorldPacket& recvPacket)
@ -314,6 +308,7 @@ void WorldSession::_HandleCharEnumOpcode(WorldPacket& recvPacket)
GetInstance()->SetError();
return;
}
_LoadCache(); // we are about to login, so we need cache data
logdetail("W: Chars in list: %u\n",num);
for(unsigned int i=0;i<num;i++){
recvPacket >> plr[i]._guid;

View File

@ -13,14 +13,8 @@
class WorldSocket;
class WorldPacket;
class Channel;
struct OpcodeHandler
{
uint16 opcode;
void (WorldSession::*handler)(WorldPacket& recvPacket);
};
class RealmSession;
struct OpcodeHandler;
class WorldSession
@ -33,17 +27,13 @@ public:
PseuInstance *GetInstance(void) { return _instance; }
SCPDatabaseMgr& GetDBMgr(void) { return GetInstance()->dbmgr; }
OpcodeHandler *_GetOpcodeHandlerTable(void) const;
void SetSocket(WorldSocket *sock);
void AddToPktQueue(WorldPacket *pkt);
void Connect(std::string addr,uint16 port);
void Update(void);
void Start(void);
bool IsValid(void) { return _valid; }
bool DeleteMe(void);
bool MustDie(void) { return _mustdie; }
void SetMustDie(void) { _mustdie = true; }
void SendWorldPacket(WorldPacket&);
bool InWorld(void) { return _logged; }
void SetTarget(uint64 guid);
uint64 GetTarget(void) { return GetMyChar()->GetTarget(); }
@ -65,6 +55,7 @@ public:
ObjMgr objmgr;
private:
OpcodeHandler *_GetOpcodeHandlerTable(void) const;
// Helpers
void _OnEnterWorld(void); // = login
@ -99,10 +90,12 @@ private:
void _ValuesUpdate(uint64 uguid, WorldPacket& recvPacket); // ...
void _QueryObjectInfo(uint64 guid);
void _LoadCache(void);
PseuInstance *_instance;
WorldSocket *_socket;
ZThread::LockedQueue<WorldPacket*,ZThread::FastMutex> pktQueue;
bool _valid,_authed,_logged,_deleteme; // world status
bool _logged,_mustdie; // world status
SocketHandler _sh; // handles the WorldSocket
Channel *_channels;
uint64 _myGUID;

View File

@ -7,32 +7,40 @@ WorldSocket::WorldSocket(SocketHandler &h, WorldSession *s) : TcpSocket(h)
{
_session = s;
_gothdr = false;
_ok=false;
}
bool WorldSocket::IsOk(void)
{
return _ok;
}
void WorldSocket::OnConnect()
{
log("Connected to world server.\r\n");
log("Connected to world server.");
_ok = true;
}
void WorldSocket::OnConnectFailed()
{
logerror("WorldSocket::OnConnectFailed()\n");
if(_session)
_session->SetSocket(NULL);
logerror("Connecting to World Server failed!");
_ok = false;
}
void WorldSocket::OnDelete()
{
log("Connection to world server has been closed.\n");
if(_session)
_session->SetSocket(NULL);
log("Connection to world server has been closed.");
_ok = false;
_session->SetMustDie(); // recreate the session if needed
}
void WorldSocket::OnException()
{
DEBUG(logdebug("WorldSocket::OnException()"));
this->SetCloseAndDelete();
if(_ok)
{
logerror("WorldSocket::OnException()");
_ok = false;
}
}
void WorldSocket::OnRead()
@ -100,6 +108,8 @@ void WorldSocket::OnRead()
void WorldSocket::SendWorldPacket(WorldPacket &pkt)
{
if(!_ok)
return;
ClientPktHeader hdr;
memset(&hdr,0,sizeof(ClientPktHeader));
hdr.size = ntohs(pkt.size()+4);

View File

@ -1,7 +1,7 @@
#ifndef _WORLDSOCKET_H
#define _WORLDSOCKET_H
#include "Network/ResolvSocket.h"
#include "Network/TcpSocket.h"
#include "SysDefs.h"
class WorldSession;
@ -24,6 +24,7 @@ class WorldSocket : public TcpSocket
public:
WorldSocket(SocketHandler &h, WorldSession *s);
WorldSession *GetSession(void) { return _session; }
bool IsOk();
void OnRead();
void OnConnect();
@ -40,6 +41,7 @@ private:
bool _gothdr; // true if only the header was recieved yet
uint16 _opcode; // stores the last recieved opcode
uint16 _remaining; // bytes amount of the next data packet
bool _ok;
};

View File

@ -383,6 +383,12 @@
<Filter
Name="Realm"
Filter="">
<File
RelativePath=".\Client\Realm\RealmSession.cpp">
</File>
<File
RelativePath=".\Client\Realm\RealmSession.h">
</File>
<File
RelativePath=".\Client\Realm\RealmSocket.cpp">
</File>

View File

@ -123,6 +123,9 @@
<Filter
Name="Source Files"
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
<File
RelativePath=".\shared\ByteBuffer.h">
</File>
<File
RelativePath=".\shared\common.h">
</File>

View File

@ -55,6 +55,7 @@ SocketHandler::SocketHandler(StdLog *p)
,m_bTryDirect(false)
,m_resolv_id(0)
,m_resolver(NULL)
,m_auto_close_sockets(true)
{
FD_ZERO(&m_rfds);
FD_ZERO(&m_wfds);
@ -68,16 +69,20 @@ SocketHandler::~SocketHandler()
m_resolver -> Quit();
if (!m_slave)
{
for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
if(m_auto_close_sockets)
{
Socket *p = (*it).second;
p -> Close();
// p -> OnDelete(); // hey, I turn this back on. what's the worst that could happen??!!
// MinionSocket breaks, calling MinderHandler methods in OnDelete -
// MinderHandler is already gone when that happens...
if (p -> DeleteByHandler())
for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
{
delete p;
Socket *p = (*it).second;
if(p)
{
p -> Close();
p -> OnDelete();
if (p -> DeleteByHandler())
{
delete p;
}
}
}
}
}

View File

@ -42,6 +42,8 @@ class SocketHandler
SocketHandler(StdLog * = NULL);
virtual ~SocketHandler();
void SetAutoCloseSockets(bool x=true) { m_auto_close_sockets = x; }
/** Register StdLog object for error callback. */
void RegStdLog(StdLog *);
void LogError(Socket *,const std::string&,int,const std::string&,loglevel_t = LOG_LEVEL_WARNING);
@ -119,5 +121,6 @@ class SocketHandler
int m_resolv_id;
ResolvServer *m_resolver;
port_t m_resolver_port;
bool m_auto_close_sockets;
};
#endif // _SOCKETHANDLER_H

View File

@ -257,10 +257,9 @@ void ExtractMaps(void)
fh.write((char*)bb.contents(),bb.size());
fh.close();
extr++;
printf("Map [%u/%u]: %s: %u\r",it+1,mapNames.size(),mapNames[it].c_str(),extr);
}
}
printf("Map [%u/%u]: %s: %u\r",it+1,mapNames.size(),mapNames[it].c_str(),extr);
}
}
extrtotal+=extr;