From 97f5d0da987a940b746616543d0c5d7192916459 Mon Sep 17 00:00:00 2001 From: "False.Genesis" Date: Wed, 29 Aug 2007 18:35:12 +0000 Subject: [PATCH] * implemented patch download supplied by realm server (does NOT yet support resuming a download) * fixed a few bugs in RealmSession (ticket #14, thx bLuma) * fixed a mistake in DefScript.cpp added in prev commit. * added wrapper class for MD5 hashes --- src/Client/DefScript/DefScript.cpp | 2 +- src/Client/PseuWoW.h | 1 - src/Client/Realm/RealmSession.cpp | 240 ++++++++++++++++++++++++++--- src/Client/Realm/RealmSession.h | 9 +- src/shared.vcproj | 3 + src/shared/Auth/MD5Hash.h | 45 ++++++ 6 files changed, 277 insertions(+), 23 deletions(-) create mode 100644 src/shared/Auth/MD5Hash.h diff --git a/src/Client/DefScript/DefScript.cpp b/src/Client/DefScript/DefScript.cpp index 5ecaf1a..77e1508 100644 --- a/src/Client/DefScript/DefScript.cpp +++ b/src/Client/DefScript/DefScript.cpp @@ -275,7 +275,7 @@ bool DefScriptPackage::LoadScriptFromFile(std::string fn){ DeleteScript(curScript->GetName()); sn = stringToLower(value); _UpdateOrCreateScriptByName(sn); - _DEFSC_DEBUG_LOG("DefScript: now loading '%s'\n",sn.c_str()); + _DEFSC_DEBUG(printf("DefScript: now loading '%s'\n",sn.c_str())); curScript=Script[sn]; } else if(line=="debug") diff --git a/src/Client/PseuWoW.h b/src/Client/PseuWoW.h index c20d3db..ad63f0d 100644 --- a/src/Client/PseuWoW.h +++ b/src/Client/PseuWoW.h @@ -92,7 +92,6 @@ public: inline void Stop(void) { _stop = true; } inline bool Stopped(void) { return _stop; } inline void SetFastQuit(bool q=true) { _fastquit=true; } - void Quit(void); void Run(void); void Update(void); void Sleep(uint32 msecs); diff --git a/src/Client/Realm/RealmSession.cpp b/src/Client/Realm/RealmSession.cpp index 4918016..716f859 100644 --- a/src/Client/Realm/RealmSession.cpp +++ b/src/Client/Realm/RealmSession.cpp @@ -7,11 +7,42 @@ enum AuthCmd { + //AUTH_NO_CMD = 0xFF, AUTH_LOGON_CHALLENGE = 0x00, AUTH_LOGON_PROOF = 0x01, + //AUTH_RECONNECT_CHALLENGE = 0x02, + //AUTH_RECONNECT_PROOF = 0x03, + //update srv =4 REALM_LIST = 0x10, + XFER_INITIATE = 0x30, + XFER_DATA = 0x31, + XFER_ACCEPT = 0x32, + XFER_RESUME = 0x33, + XFER_CANCEL = 0x34 }; +enum eAuthResults +{ + REALM_AUTH_SUCCESS = 0, + REALM_AUTH_FAILURE=0x01, ///< Unable to connect + REALM_AUTH_UNKNOWN1=0x02, ///< Unable to connect + REALM_AUTH_ACCOUNT_BANNED=0x03, ///< This account has been closed and is no longer available for use. Please go to /banned.html for further information. + REALM_AUTH_NO_MATCH=0x04, ///< The information you have entered is not valid. Please check the spelling of the account name and password. If you need help in retrieving a lost or stolen password, see for more information + REALM_AUTH_UNKNOWN2=0x05, ///< The information you have entered is not valid. Please check the spelling of the account name and password. If you need help in retrieving a lost or stolen password, see for more information + REALM_AUTH_ACCOUNT_IN_USE=0x06, ///< This account is already logged into . Please check the spelling and try again. + REALM_AUTH_PREPAID_TIME_LIMIT=0x07, ///< You have used up your prepaid time for this account. Please purchase more to continue playing + REALM_AUTH_SERVER_FULL=0x08, ///< Could not log in to at this time. Please try again later. + REALM_AUTH_WRONG_BUILD_NUMBER=0x09, ///< Unable to validate game version. This may be caused by file corruption or interference of another program. Please visit for more information and possible solutions to this issue. + REALM_AUTH_UPDATE_CLIENT=0x0a, ///< Downloading + REALM_AUTH_UNKNOWN3=0x0b, ///< Unable to connect + REALM_AUTH_ACCOUNT_FREEZED=0x0c, ///< This account has been temporarily suspended. Please go to /banned.html for further information + REALM_AUTH_UNKNOWN4=0x0d, ///< Unable to connect + REALM_AUTH_UNKNOWN5=0x0e, ///< Connected. + REALM_AUTH_PARENTAL_CONTROL=0x0f ///< Access to this account has been blocked by parental controls. Your settings may be changed in your account preferences at +}; + +#define ChunkSize 2048 + struct SRealmHeader { uint8 cmd; // OP code = CMD_REALM_LIST @@ -42,8 +73,8 @@ struct AuthHandler struct sAuthLogonChallenge_S { uint8 cmd; - uint8 error; uint8 unk2; + uint8 error; uint8 B[32]; uint8 g_len; uint8 g[1]; @@ -66,6 +97,8 @@ RealmSession::RealmSession(PseuInstance* instance) _instance = instance; _socket = NULL; _mustdie = false; + _filetransfer = false; + _file_size = 0; _sh.SetAutoCloseSockets(false); } @@ -121,6 +154,8 @@ AuthHandler *RealmSession::_GetAuthHandlerTable(void) const {AUTH_LOGON_CHALLENGE,&RealmSession::_HandleLogonChallenge}, {AUTH_LOGON_PROOF,&RealmSession::_HandleLogonProof}, {REALM_LIST,&RealmSession::_HandleRealmList}, + {XFER_INITIATE,&RealmSession::_HandleTransferInit}, + {XFER_DATA,&RealmSession::_HandleTransferData}, {0,NULL} }; return table; @@ -136,6 +171,7 @@ void RealmSession::Update(void) AuthHandler *table = _GetAuthHandlerTable(); ByteBuffer *pkt; uint8 cmd; + bool valid = true; if( _sh.GetCount() ) // the socket will remove itself from the handler if it got closed _sh.Select(0,0); @@ -149,26 +185,43 @@ void RealmSession::Update(void) while(pktQueue.size()) { + valid = false; pkt = pktQueue.next(); cmd = (*pkt)[0]; - for(uint8 i=0;table[i].handler!=NULL;i++) + + // this is a dirty hack for oversize/splitted up packets that are buffered wrongly by realmd + if(_filetransfer) { - if(table[i].cmd==cmd) + _HandleTransferData(*pkt); + } + // if we dont expect a file transfer select packets as usual + else + { + for(uint8 i=0;table[i].handler!=NULL;i++) { - (this->*table[i].handler)(*pkt); - if(pkt->rpos() < pkt->size()) + if(table[i].cmd==cmd) { - 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; + valid = true; + (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; + } + break; } - delete pkt; + } + if(!valid) + { + logerror("Invalid realm packet, unknown opcode 0x%X",cmd); + //logerror(toHexDump((uint8*)pkt->contents(),pkt->size()).c_str()); } } - + delete pkt; } } @@ -251,7 +304,7 @@ 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!"); + logcritical("Missing data, can't send Login challenge to Realm Server! (check your conf files)"); GetInstance()->SetError(); return; } @@ -279,7 +332,7 @@ void RealmSession::SendLogonChallenge(void) 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)) + if(pkt.size() < 3) { logerror("AUTH_LOGON_CHALLENGE: Recieved incorrect/unknown packet. Hexdump:"); DumpInvalidPacket(pkt); @@ -287,22 +340,23 @@ void RealmSession::_HandleLogonChallenge(ByteBuffer& pkt) } sAuthLogonChallenge_S lc; - pkt.read((uint8*)&lc, sizeof(sAuthLogonChallenge_S)); + lc.error = pkt[2]; // pre-set error (before copying whole challenge) switch (lc.error) { case 4: - log("Realm Server did not find account \"%s\"!",GetInstance()->GetConf()->accname.c_str()); + logerror("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()); + logerror("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!"); + logerror("Realm Server doesn't accept this version!"); break; case 0: { + pkt.read((uint8*)&lc, sizeof(sAuthLogonChallenge_S)); logdetail("Login successful, now calculating proof packet..."); // now lets start calculating @@ -441,7 +495,7 @@ void RealmSession::_HandleLogonChallenge(ByteBuffer& pkt) void RealmSession::_HandleLogonProof(ByteBuffer& pkt) { logdebug("RealmSocket: Got AUTH_LOGON_PROOF [%u of %u bytes]\n",pkt.size(),26); - if(pkt.size() < 26) + if(pkt.size() < 2) { logerror("AUTH_LOGON_PROOF: Recieved incorrect/unknown packet. Hexdump:"); DumpInvalidPacket(pkt); @@ -449,6 +503,32 @@ void RealmSession::_HandleLogonProof(ByteBuffer& pkt) SetMustDie(); return; } + uint8 error = pkt[1]; + + // handle error codes + switch(error) + { + case REALM_AUTH_UPDATE_CLIENT: + log("The realm server requested client update."); + return; + + case REALM_AUTH_NO_MATCH: + case REALM_AUTH_UNKNOWN2: + logerror("Wrong password or invalid account information or authentication error"); + return; + + // cover all other cases. continue only if success. + default: + if(error != REALM_AUTH_SUCCESS) + { + logerror("AUTH_LOGON_PROOF: unk error = 0x%X",error); + pkt.rpos(2); + 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); @@ -473,6 +553,126 @@ void RealmSession::_HandleLogonProof(ByteBuffer& pkt) } } +void RealmSession::_HandleTransferInit(ByteBuffer& pkt) +{ + _filebuf.clear(); + _transbuf.clear(); + _file_done = 0; + _filetransfer = true; + + uint8 cmd; + uint8 type_size; + uint8 *type_str; + + pkt >> cmd >> type_size; + type_str = new uint8[type_size+1]; + type_str[type_size] = 0; + pkt.read(type_str,type_size); + pkt >> _file_size; + pkt.read(_file_md5,MD5_DIGEST_LENGTH); + logcustom(0,GREEN,"TransferInit [%s]: File size: "I64FMTD" KB (MD5: %s)", (char*)type_str, _file_size / 1024L, toHexDump(&_file_md5[0],MD5_DIGEST_LENGTH,false).c_str()); + delete [] type_str; + ByteBuffer bb(1); + bb << uint8(XFER_ACCEPT); + SendRealmPacket(bb); + logdebug("XFER_ACCEPT sent"); +} + +void RealmSession::_HandleTransferData(ByteBuffer& pkt) +{ + if(!_file_size) + { + logerror("Realm server attempted to transfer a file, but didn't init!"); + SetMustDie(); + return; + } + + uint8 cmd; + uint16 size; + uint8 *data; + + _transbuf.append(pkt.contents(),pkt.size()); // append everything to the transfer buffer, which may also store incomplete bytes from the packet before + pkt.rpos(pkt.size()); // set rpos to the end of the packet to indicate that we used all data + + logdev("transbuf size=%u rpos=%u diff=%u",_transbuf.size(),_transbuf.rpos(),_transbuf.size() - _transbuf.rpos()); + + while( _transbuf.size() - _transbuf.rpos() >= 3) // 3 = sizeof(uint32)+sizeof(uint8) + { + _transbuf >> cmd >> size; + if(_transbuf.size()-_transbuf.rpos() < size) + { + _transbuf.rpos(_transbuf.rpos()-3); // read the header next time again + break; // packet parts missing, continue after recieving next packet + } + data = new uint8[size]; + _transbuf.read(data,size); + _filebuf.append(data,size); + _file_done += size; + delete [] data; + float pct = ((float)_file_done / (float)_file_size * 100.0f); + + // use better output formatting in debug level + if(GetInstance()->GetConf()->debug >= 2) + logdebug("Got data packet, %u data bytes. [%.2f%% done] cmd 0x%X",size,pct,cmd); + else + { + _log_setcolor(true,GREEN); + printf("\r[%.2f%% done]",pct); + _log_resetcolor(true); + } + + } + + // finalize file + if(_file_done >= _file_size) + { + log(""); + log("File transfer finished."); + _filetransfer = false; + MD5Hash md5h; + md5h.Update((uint8*)_filebuf.contents(),_filebuf.size()); + md5h.Finalize(); + std::string md5hex = toHexDump(md5h.GetDigest(),md5h.GetLength(),false); + logdebug("MD5 hash: %s", md5hex.c_str()); + if(!memcmp(_file_md5, md5h.GetDigest(), md5h.GetLength())) + { + std::fstream fh; + char namebuf[100]; + sprintf(namebuf,"%u_%s.mpq",GetInstance()->GetConf()->clientbuild,GetInstance()->GetConf()->clientlang.c_str()); + fh.open(namebuf,std::ios_base::out | std::ios_base::binary); + if(fh.is_open()) + { + fh.write((const char*)_filebuf.contents(),_filebuf.size()); + fh.close(); + log("File saved as \"%s\"",namebuf); + } + else + { + logerror("Could not save \"%s\"",namebuf); + } + } + else + { + logerror("File corruption! Transfer failed! (MD5: %s",md5hex.c_str()); + } + _transbuf.clear(); + + // client sends cancel after successful file transfer also + ByteBuffer bb(1); + bb << uint8(XFER_CANCEL); + SendRealmPacket(bb); + + log("Now modify your conf files and restart PseuWoW."); + for(int8 x = 3; x > -1; x--) // add little delay + { + printf("exiting in... [%u]\r",x); + GetInstance()->Sleep(1000); + } + SetMustDie(); + GetInstance()->Stop(); + } +} + void RealmSession::DumpInvalidPacket(ByteBuffer& pkt) { if(pkt.size()) diff --git a/src/Client/Realm/RealmSession.h b/src/Client/Realm/RealmSession.h index b4590a8..8224443 100644 --- a/src/Client/Realm/RealmSession.h +++ b/src/Client/Realm/RealmSession.h @@ -2,6 +2,7 @@ #define REALMSESSION_H #include "common.h" +#include "Auth/MD5Hash.h" struct AuthHandler; class RealmSocket; @@ -26,6 +27,8 @@ private: void _HandleRealmList(ByteBuffer&); void _HandleLogonProof(ByteBuffer&); void _HandleLogonChallenge(ByteBuffer&); + void _HandleTransferInit(ByteBuffer&); + void _HandleTransferData(ByteBuffer&); AuthHandler *_GetAuthHandlerTable(void) const; void SendRealmPacket(ByteBuffer&); void DumpInvalidPacket(ByteBuffer&); @@ -38,7 +41,11 @@ private: RealmSession *_session; BigNumber _key; bool _mustdie; - + bool _filetransfer; + uint8 _file_md5[MD5_DIGEST_LENGTH]; + uint64 _file_done, _file_size; + ByteBuffer _filebuf; + ByteBuffer _transbuf; // stores parts of unfinished packets }; diff --git a/src/shared.vcproj b/src/shared.vcproj index e696250..88d7012 100644 --- a/src/shared.vcproj +++ b/src/shared.vcproj @@ -298,6 +298,9 @@ + + diff --git a/src/shared/Auth/MD5Hash.h b/src/shared/Auth/MD5Hash.h new file mode 100644 index 0000000..9a6339d --- /dev/null +++ b/src/shared/Auth/MD5Hash.h @@ -0,0 +1,45 @@ +#ifndef MD5HASH_H +#define MD5HASH_H + +#define MD5_DIGEST_LENGTH 16 + +#include "Auth/md5.h" + +class MD5Hash +{ +public: + MD5Hash() + { + md5_init(&_state); + memset(_digest,0,MD5_DIGEST_LENGTH); + } + + + void Update(uint8 *buf,uint32 len) + { + md5_append(&_state,buf,len); + } + + void Update(std::string s) + { + Update((uint8*)s.c_str(),s.length()); + } + + void Finalize(void) + { + md5_finish(&_state,_digest); + } + + uint8 *GetDigest(void) + { + return _digest; + } + + uint8 GetLength(void) { return MD5_DIGEST_LENGTH; } + +private: + md5_state_t _state; + uint8 _digest[MD5_DIGEST_LENGTH]; +}; + +#endif