From 6c4ae79a878b1fd795364e600740f6d104c542ca Mon Sep 17 00:00:00 2001 From: "False.Genesis" Date: Fri, 11 May 2007 20:55:38 +0000 Subject: [PATCH] * 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. --- bin/conf/PseuWoW.conf.default | 14 +- bin/data/scp/class.scp | 2 +- src/Client/DefScript/DefScript.cpp | 3 +- src/Client/DefScriptInterface.cpp | 28 +- src/Client/PseuWoW.cpp | 141 +++---- src/Client/PseuWoW.h | 13 +- src/Client/Realm/RealmSession.cpp | 489 ++++++++++++++++++++++++ src/Client/Realm/RealmSession.h | 45 +++ src/Client/Realm/RealmSocket.cpp | 465 ++-------------------- src/Client/Realm/RealmSocket.h | 40 +- src/Client/World/CMSGConstructor.cpp | 2 +- src/Client/World/WorldSession.cpp | 97 +++-- src/Client/World/WorldSession.h | 25 +- src/Client/World/WorldSocket.cpp | 30 +- src/Client/World/WorldSocket.h | 4 +- src/PseuWoW.vcproj | 6 + src/shared.vcproj | 3 + src/shared/Network/SocketHandler.cpp | 21 +- src/shared/Network/SocketHandler.h | 3 + src/tools/stuffextract/StuffExtract.cpp | 3 +- 20 files changed, 796 insertions(+), 638 deletions(-) create mode 100644 src/Client/Realm/RealmSession.cpp create mode 100644 src/Client/Realm/RealmSession.h diff --git a/bin/conf/PseuWoW.conf.default b/bin/conf/PseuWoW.conf.default index 9858f04..c76957f 100644 --- a/bin/conf/PseuWoW.conf.default +++ b/bin/conf/PseuWoW.conf.default @@ -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 + diff --git a/bin/data/scp/class.scp b/bin/data/scp/class.scp index f93ed36..ca6505c 100644 --- a/bin/data/scp/class.scp +++ b/bin/data/scp/class.scp @@ -23,7 +23,7 @@ name=Shaman name=Mage [9] -name=Paladin +name=Warlock [10] name=UNKNOWN CLASS(10) diff --git a/src/Client/DefScript/DefScript.cpp b/src/Client/DefScript/DefScript.cpp index 8aa4987..d1bc185 100644 --- a/src/Client/DefScript/DefScript.cpp +++ b/src/Client/DefScript/DefScript.cpp @@ -30,7 +30,8 @@ DefScriptPackage::DefScriptPackage() DefScriptPackage::~DefScriptPackage() { - delete _eventmgr; + if(_eventmgr) + delete _eventmgr; Clear(); } diff --git a/src/Client/DefScriptInterface.cpp b/src/Client/DefScriptInterface.cpp index c7acee3..c7a8d25 100644 --- a/src/Client/DefScriptInterface.cpp +++ b/src/Client/DefScriptInterface.cpp @@ -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; diff --git a/src/Client/PseuWoW.cpp b/src/Client/PseuWoW.cpp index 0d321d3..85a19a2 100644 --- a/src/Client/PseuWoW.cpp +++ b/src/Client/PseuWoW.cpp @@ -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()); diff --git a/src/Client/PseuWoW.h b/src/Client/PseuWoW.h index 4a32ffc..bcb3ae8 100644 --- a/src/Client/PseuWoW.h +++ b/src/Client/PseuWoW.h @@ -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; diff --git a/src/Client/Realm/RealmSession.cpp b/src/Client/Realm/RealmSession.cpp new file mode 100644 index 0000000..65da43f --- /dev/null +++ b/src/Client/Realm/RealmSession.cpp @@ -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> 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;iGetConf()->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!"); + } +} + + + + + diff --git a/src/Client/Realm/RealmSession.h b/src/Client/Realm/RealmSession.h new file mode 100644 index 0000000..b4590a8 --- /dev/null +++ b/src/Client/Realm/RealmSession.h @@ -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 pktQueue; + RealmSocket *_socket; + uint8 _m2[20]; + RealmSession *_session; + BigNumber _key; + bool _mustdie; + +}; + + +#endif \ No newline at end of file diff --git a/src/Client/Realm/RealmSocket.cpp b/src/Client/Realm/RealmSocket.cpp index bcad7c9..9eae3ca 100644 --- a/src/Client/Realm/RealmSocket.cpp +++ b/src/Client/Realm/RealmSocket.cpp @@ -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> 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;iGetConf()->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; +} + diff --git a/src/Client/Realm/RealmSocket.h b/src/Client/Realm/RealmSocket.h index e4ea332..1aadda0 100644 --- a/src/Client/Realm/RealmSocket.h +++ b/src/Client/Realm/RealmSocket.h @@ -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; }; diff --git a/src/Client/World/CMSGConstructor.cpp b/src/Client/World/CMSGConstructor.cpp index 1c96225..789456b 100644 --- a/src/Client/World/CMSGConstructor.cpp +++ b/src/Client/World/CMSGConstructor.cpp @@ -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< 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> plr[i]._guid; diff --git a/src/Client/World/WorldSession.h b/src/Client/World/WorldSession.h index cc8c8a7..6ccbcf9 100644 --- a/src/Client/World/WorldSession.h +++ b/src/Client/World/WorldSession.h @@ -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 pktQueue; - bool _valid,_authed,_logged,_deleteme; // world status + bool _logged,_mustdie; // world status SocketHandler _sh; // handles the WorldSocket Channel *_channels; uint64 _myGUID; diff --git a/src/Client/World/WorldSocket.cpp b/src/Client/World/WorldSocket.cpp index 995095f..84e4bfe 100644 --- a/src/Client/World/WorldSocket.cpp +++ b/src/Client/World/WorldSocket.cpp @@ -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); diff --git a/src/Client/World/WorldSocket.h b/src/Client/World/WorldSocket.h index a341615..9f38c6b 100644 --- a/src/Client/World/WorldSocket.h +++ b/src/Client/World/WorldSocket.h @@ -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; }; diff --git a/src/PseuWoW.vcproj b/src/PseuWoW.vcproj index 9e0aa37..268f084 100644 --- a/src/PseuWoW.vcproj +++ b/src/PseuWoW.vcproj @@ -383,6 +383,12 @@ + + + + diff --git a/src/shared.vcproj b/src/shared.vcproj index 65fe85d..26baafe 100644 --- a/src/shared.vcproj +++ b/src/shared.vcproj @@ -123,6 +123,9 @@ + + diff --git a/src/shared/Network/SocketHandler.cpp b/src/shared/Network/SocketHandler.cpp index 7f5c904..91d1c32 100644 --- a/src/shared/Network/SocketHandler.cpp +++ b/src/shared/Network/SocketHandler.cpp @@ -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; + } + } } } } diff --git a/src/shared/Network/SocketHandler.h b/src/shared/Network/SocketHandler.h index a0df2e2..83207b5 100644 --- a/src/shared/Network/SocketHandler.h +++ b/src/shared/Network/SocketHandler.h @@ -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 diff --git a/src/tools/stuffextract/StuffExtract.cpp b/src/tools/stuffextract/StuffExtract.cpp index 79c782c..e63121d 100644 --- a/src/tools/stuffextract/StuffExtract.cpp +++ b/src/tools/stuffextract/StuffExtract.cpp @@ -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;