diff --git a/sql/realmd.sql b/sql/realmd.sql index 2b8bc64a3..03f604064 100644 --- a/sql/realmd.sql +++ b/sql/realmd.sql @@ -21,7 +21,7 @@ DROP TABLE IF EXISTS `realmd_db_version`; CREATE TABLE `realmd_db_version` ( - `required_8728_01_realmd_account` bit(1) default NULL + `required_9010_01_realmd_realmlist` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB'; -- @@ -161,6 +161,7 @@ CREATE TABLE `realmlist` ( `timezone` tinyint(3) unsigned NOT NULL default '0', `allowedSecurityLevel` tinyint(3) unsigned NOT NULL default '0', `population` float unsigned NOT NULL default '0', + `realmbuilds` varchar(64) NOT NULL default '', PRIMARY KEY (`id`), UNIQUE KEY `idx_name` (`name`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='Realm System'; diff --git a/sql/updates/9010_01_realmd_realmlist.sql b/sql/updates/9010_01_realmd_realmlist.sql new file mode 100644 index 000000000..223e5d678 --- /dev/null +++ b/sql/updates/9010_01_realmd_realmlist.sql @@ -0,0 +1,4 @@ +ALTER TABLE realmd_db_version CHANGE COLUMN required_8728_01_realmd_account required_9010_01_realmd_realmlist bit; + +ALTER TABLE realmlist + ADD COLUMN realmbuilds varchar(64) NOT NULL default '' AFTER population; diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 99b16e23c..56d235f6d 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -211,6 +211,7 @@ pkgdata_DATA = \ 9001_01_mangos_spell_proc_event.sql \ 9005_01_mangos_spell_proc_event.sql \ 9007_01_mangos_spell_proc_event.sql \ + 9010_01_realmd_realmlist.sql \ README ## Additional files to include when running 'make dist' @@ -402,4 +403,5 @@ EXTRA_DIST = \ 9001_01_mangos_spell_proc_event.sql \ 9005_01_mangos_spell_proc_event.sql \ 9007_01_mangos_spell_proc_event.sql \ + 9010_01_realmd_realmlist.sql \ README diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index 9052f386b..7f8fe3728 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -2640,4 +2640,10 @@ enum PetTameFailureReason PETTAME_UNKNOWNERROR = 12 }; +// we need to stick to 1 version or half of the stuff will work for someone +// others will not and opposite +// will only support WoW:WotLK 3.2.2a, client build 10505. + +#define EXPECTED_MANGOSD_CLIENT_BUILD {10505, 0} + #endif diff --git a/src/game/WorldSocket.cpp b/src/game/WorldSocket.cpp index f93804e58..837ba5c44 100644 --- a/src/game/WorldSocket.cpp +++ b/src/game/WorldSocket.cpp @@ -745,7 +745,7 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) BigNumber K; // Read the content of the packet - recvPacket >> BuiltNumberClient; // for now no use + recvPacket >> BuiltNumberClient; recvPacket >> unk2; recvPacket >> account; recvPacket >> unk3; @@ -760,6 +760,29 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) unk3, clientSeed); + // Check the version of client trying to connect + bool valid_version = false; + int accepted_versions[] = EXPECTED_MANGOSD_CLIENT_BUILD; + for(int i = 0; accepted_versions[i]; ++i) + { + if(BuiltNumberClient == accepted_versions[i]) + { + valid_version = true; + break; + } + } + + if(!valid_version) + { + packet.Initialize (SMSG_AUTH_RESPONSE, 1); + packet << uint8 (AUTH_VERSION_MISMATCH); + + SendPacket (packet); + + sLog.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (version mismatch)."); + return -1; + } + // Get the account information from the realmd database std::string safe_account = account; // Duplicate, else will screw the SHA hash verification below loginDatabase.escape_string (safe_account); diff --git a/src/mangosd/Master.cpp b/src/mangosd/Master.cpp index 829725f32..d4c60d5dd 100644 --- a/src/mangosd/Master.cpp +++ b/src/mangosd/Master.cpp @@ -226,9 +226,17 @@ int Master::Run() ///- Launch WorldRunnable thread ACE_Based::Thread world_thread(new WorldRunnable); world_thread.setPriority(ACE_Based::Highest); - - // set server online - loginDatabase.PExecute("UPDATE realmlist SET color = 0, population = 0 WHERE id = '%d'",realmID); + + // set realmbuilds depend on mangosd expected builds, and set server online + { + std::ostringstream data; + int accepted_versions[] = EXPECTED_MANGOSD_CLIENT_BUILD; + for(int i = 0; accepted_versions[i]; ++i) + { + data << accepted_versions[i] << " "; + } + loginDatabase.PExecute("UPDATE realmlist SET color = 0, population = 0, realmbuilds = '%s' WHERE id = '%d'", data.str().c_str(), realmID); + } ACE_Based::Thread* cliThread = NULL; diff --git a/src/realmd/AuthCodes.h b/src/realmd/AuthCodes.h index 7df23ad42..1e104b3d8 100644 --- a/src/realmd/AuthCodes.h +++ b/src/realmd/AuthCodes.h @@ -64,11 +64,18 @@ enum LoginResult LOGIN_LOCKED_ENFORCED = 0x10, }; -// we need to stick to 1 version or half of the stuff will work for someone -// others will not and opposite -// will only support WoW, WoW:TBC and WoW:WotLK 3.2.2a client build 10505... +// will only support WoW 1.12.1/1.12.2 , WoW:TBC 2.4.3 and WoW:WotLK 3.2.2a, client builds 10505, 8606, 6005, 5875 +// if you need more from old build then add it in cases in relamd sources code +// list sorted from high to low build and first build used as low bound for accepted by default range (any > it will accepted by realmd at least) -#define EXPECTED_MANGOS_CLIENT_BUILD {10505, 0} +#define EXPECTED_REALMD_CLIENT_BUILD \ +{ \ + 10505, /* 3.2.2a and higher */ \ + 8606, /* 2.4.3 */ \ + 6005, /* 1.12.2 */ \ + 5875, /* 1.12.1 */ \ + 0 \ +} // At update excepted builds please update if need define DEFAULT_MAX_LEVEL // in DBCEnum.h to default max player level expected by build diff --git a/src/realmd/AuthSocket.cpp b/src/realmd/AuthSocket.cpp index cdbccb19c..3f75e872e 100644 --- a/src/realmd/AuthSocket.cpp +++ b/src/realmd/AuthSocket.cpp @@ -22,14 +22,12 @@ #include "Common.h" #include "Database/DatabaseEnv.h" -#include "ByteBuffer.h" #include "Config/ConfigEnv.h" #include "Log.h" #include "RealmList.h" #include "AuthSocket.h" #include "AuthCodes.h" #include -#include "Auth/Sha1.h" //#include "Util.h" -- for commented utf8ToUpperOnlyLatin extern DatabaseType loginDatabase; @@ -129,6 +127,16 @@ typedef struct AUTH_LOGON_PROOF_S uint16 unk3; } sAuthLogonProof_S; +typedef struct AUTH_LOGON_PROOF_S_BUILD_6005 +{ + uint8 cmd; + uint8 error; + uint8 M2[20]; + //uint32 unk1; + uint32 unk2; + //uint16 unk3; +} sAuthLogonProof_S_BUILD_6005; + typedef struct AUTH_RECONNECT_PROOF_C { uint8 cmd; @@ -321,6 +329,40 @@ void AuthSocket::_SetVSFields(const std::string& rI) OPENSSL_free((void*)s_hex); } +void AuthSocket::SendProof(Sha1Hash sha) +{ + switch(_build) + { + case 5875: // 1.12.1 + case 6005: // 1.12.2 + { + sAuthLogonProof_S_BUILD_6005 proof; + memcpy(proof.M2, sha.GetDigest(), 20); + proof.cmd = AUTH_LOGON_PROOF; + proof.error = 0; + proof.unk2 = 0x00; + + SendBuf((char *)&proof, sizeof(proof)); + break; + } + case 8606: // 2.4.3 + case 10505: // 3.2.2a + default: // or later + { + sAuthLogonProof_S proof; + memcpy(proof.M2, sha.GetDigest(), 20); + proof.cmd = AUTH_LOGON_PROOF; + proof.error = 0; + proof.unk1 = 0x00800000; + proof.unk2 = 0x00; + proof.unk3 = 0x00; + + SendBuf((char *)&proof, sizeof(proof)); + break; + } + } +} + /// Logon Challenge command handler bool AuthSocket::_HandleLogonChallenge() { @@ -346,6 +388,11 @@ bool AuthSocket::_HandleLogonChallenge() buf[buf.size() - 1] = 0; sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0]; + ///- Read the remaining of the packet + ibuf.Read((char *)&buf[4], remaining); + DEBUG_LOG("[AuthChallenge] got full packet, %#04x bytes", ch->size); + DEBUG_LOG("[AuthChallenge] name(%d): '%s'", ch->I_len, ch->I); + // BigEndian code, nop in little endian case // size already converted EndianConvert(*((uint32*)(&ch->gamename[0]))); @@ -356,11 +403,6 @@ bool AuthSocket::_HandleLogonChallenge() EndianConvert(ch->timezone_bias); EndianConvert(ch->ip); - ///- Read the remaining of the packet - ibuf.Read((char *)&buf[4], remaining); - DEBUG_LOG("[AuthChallenge] got full packet, %#04x bytes", ch->size); - DEBUG_LOG("[AuthChallenge] name(%d): '%s'", ch->I_len, ch->I); - ByteBuffer pkt; _login = (const char*)ch->I; @@ -536,13 +578,19 @@ bool AuthSocket::_HandleLogonProof() ///- Check if the client has one of the expected version numbers bool valid_version = false; - int accepted_versions[] = EXPECTED_MANGOS_CLIENT_BUILD; - for(int i = 0; accepted_versions[i]; ++i) + int accepted_versions[] = EXPECTED_REALMD_CLIENT_BUILD; + if (_build >= accepted_versions[0]) // first build is low bound of always accepted range + valid_version = true; + else { - if(_build == accepted_versions[i]) + // continue from 1 with explict equal check + for(int i = 1; accepted_versions[i]; ++i) { - valid_version = true; - break; + if(_build == accepted_versions[i]) + { + valid_version = true; + break; + } } } @@ -690,16 +738,8 @@ bool AuthSocket::_HandleLogonProof() sha.Initialize(); sha.UpdateBigNumbers(&A, &M, &K, NULL); sha.Finalize(); - - sAuthLogonProof_S proof; - memcpy(proof.M2, sha.GetDigest(), 20); - proof.cmd = AUTH_LOGON_PROOF; - proof.error = 0; - proof.unk1 = 0x00800000; - proof.unk2 = 0x00; - proof.unk3 = 0x00; - - SendBuf((char *)&proof, sizeof(proof)); + + SendProof(sha); ///- Set _authed to true! _authed = true; @@ -882,39 +922,8 @@ bool AuthSocket::_HandleRealmList() ///- Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm) ByteBuffer pkt; - pkt << (uint32) 0; - pkt << (uint16) sRealmList.size(); - RealmList::RealmMap::const_iterator i; - for( i = sRealmList.begin(); i != sRealmList.end(); ++i ) - { - uint8 AmountOfCharacters; - - // No SQL injection. id of realm is controlled by the database. - result = loginDatabase.PQuery( "SELECT numchars FROM realmcharacters WHERE realmid = '%d' AND acctid='%u'",i->second.m_ID,id); - if( result ) - { - Field *fields = result->Fetch(); - AmountOfCharacters = fields[0].GetUInt8(); - delete result; - } - else - AmountOfCharacters = 0; - - uint8 lock = (i->second.allowedSecurityLevel > _accountSecurityLevel) ? 1 : 0; - - pkt << i->second.icon; // realm type - pkt << lock; // if 1, then realm locked - pkt << i->second.color; // if 2, then realm is offline - pkt << i->first; - pkt << i->second.address; - pkt << i->second.populationLevel; - pkt << AmountOfCharacters; - pkt << i->second.timezone; // realm category - pkt << (uint8) 0x2C; // unk, may be realm number/id? - } - pkt << (uint8) 0x10; - pkt << (uint8) 0x00; - + LoadRealmlist(pkt, id); + ByteBuffer hdr; hdr << (uint8) REALM_LIST; hdr << (uint16)pkt.size(); @@ -925,6 +934,95 @@ bool AuthSocket::_HandleRealmList() return true; } +void AuthSocket::LoadRealmlist(ByteBuffer &pkt, uint32 acctid) +{ + switch(_build) + { + case 5875: // 1.12.1 + case 6005: // 1.12.2 + { + pkt << uint32(0); + pkt << uint8(sRealmList.size()); + + for(RealmList::RealmMap::const_iterator i = sRealmList.begin(); i != sRealmList.end(); ++i) + { + uint8 AmountOfCharacters; + + // No SQL injection. id of realm is controlled by the database. + QueryResult *result = loginDatabase.PQuery( "SELECT numchars FROM realmcharacters WHERE realmid = '%d' AND acctid='%u'", i->second.m_ID, acctid); + if( result ) + { + Field *fields = result->Fetch(); + AmountOfCharacters = fields[0].GetUInt8(); + delete result; + } + else + AmountOfCharacters = 0; + + // Show offline state for unsupported client builds + uint8 color = (std::find(i->second.realmbuilds.begin(), i->second.realmbuilds.end(), _build) != i->second.realmbuilds.end()) ? i->second.color : 2; + color = (i->second.allowedSecurityLevel > _accountSecurityLevel) ? 2 : color; + + pkt << uint32(i->second.icon); // realm type + pkt << uint8(color); // if 2, then realm is offline + pkt << i->first; // name + pkt << i->second.address; // address + pkt << float(i->second.populationLevel); + pkt << uint8(AmountOfCharacters); + pkt << uint8(i->second.timezone); // realm category + pkt << uint8(0x00); // unk, may be realm number/id? + } + + pkt << uint8(0x00); + pkt << uint8(0x02); + break; + } + + case 8606: // 2.4.3 + case 10505: // 3.2.2a + default: // and later + { + pkt << uint32(0); + pkt << uint16(sRealmList.size()); + + for(RealmList::RealmMap::const_iterator i = sRealmList.begin(); i != sRealmList.end(); ++i) + { + uint8 AmountOfCharacters; + + // No SQL injection. id of realm is controlled by the database. + QueryResult *result = loginDatabase.PQuery( "SELECT numchars FROM realmcharacters WHERE realmid = '%d' AND acctid='%u'", i->second.m_ID, acctid); + if( result ) + { + Field *fields = result->Fetch(); + AmountOfCharacters = fields[0].GetUInt8(); + delete result; + } + else + AmountOfCharacters = 0; + + uint8 lock = (i->second.allowedSecurityLevel > _accountSecurityLevel) ? 1 : 0; + + // Show offline state for unsupported client builds + uint8 color = (std::find(i->second.realmbuilds.begin(), i->second.realmbuilds.end(), _build) != i->second.realmbuilds.end()) ? i->second.color : 2; + + pkt << uint8(i->second.icon); // realm type + pkt << uint8(lock); // if 1, then realm locked + pkt << uint8(color); // if 2, then realm is offline + pkt << i->first; // name + pkt << i->second.address; // address + pkt << float(i->second.populationLevel); + pkt << uint8(AmountOfCharacters); + pkt << uint8(i->second.timezone); // realm category + pkt << uint8(0x2C); // unk, may be realm number/id? + } + + pkt << uint8(0x10); + pkt << uint8(0x00); + break; + } + } +} + /// Resume patch transfer bool AuthSocket::_HandleXferResume() { diff --git a/src/realmd/AuthSocket.h b/src/realmd/AuthSocket.h index cf82f34f7..4c892101b 100644 --- a/src/realmd/AuthSocket.h +++ b/src/realmd/AuthSocket.h @@ -31,6 +31,8 @@ #include "sockets/Utility.h" #include "sockets/Parse.h" #include "sockets/Socket.h" +#include "Auth/Sha1.h" +#include "ByteBuffer.h" /// Handle login commands class AuthSocket: public TcpSocket @@ -43,6 +45,8 @@ class AuthSocket: public TcpSocket void OnAccept(); void OnRead(); + void SendProof(Sha1Hash sha); + void LoadRealmlist(ByteBuffer &pkt, uint32 acctid); bool _HandleLogonChallenge(); bool _HandleLogonProof(); diff --git a/src/realmd/RealmList.cpp b/src/realmd/RealmList.cpp index 23255eb60..cf99f144c 100644 --- a/src/realmd/RealmList.cpp +++ b/src/realmd/RealmList.cpp @@ -22,6 +22,8 @@ #include "Common.h" #include "RealmList.h" +#include "AuthCodes.h" +#include "Util.h" // for Tokens typedef #include "Policies/SingletonImp.h" #include "Database/DatabaseEnv.h" @@ -48,11 +50,11 @@ void RealmList::Initialize(uint32 updateInterval) UpdateRealms(true); } -void RealmList::UpdateRealm( uint32 ID, const std::string& name, const std::string& address, uint32 port, uint8 icon, uint8 color, uint8 timezone, AccountTypes allowedSecurityLevel, float popu) -{ +void RealmList::UpdateRealm( uint32 ID, const std::string& name, const std::string& address, uint32 port, uint8 icon, uint8 color, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, const char* builds) +{ ///- Create new if not exist or update existed Realm& realm = m_realms[name]; - + realm.m_ID = ID; realm.icon = icon; realm.color = color; @@ -60,6 +62,15 @@ void RealmList::UpdateRealm( uint32 ID, const std::string& name, const std::stri realm.allowedSecurityLevel = allowedSecurityLevel; realm.populationLevel = popu; + Tokens tokens = StrSplit(builds, " "); + Tokens::iterator iter; + + for (iter = tokens.begin(); iter != tokens.end(); ++iter) + { + uint32 build = atol((*iter).c_str()); + realm.realmbuilds.insert(build); + } + ///- Append port to IP address. std::ostringstream ss; ss << address << ":" << port; @@ -85,7 +96,8 @@ void RealmList::UpdateRealms(bool init) { sLog.outDetail("Updating Realm List..."); - QueryResult *result = loginDatabase.Query( "SELECT id, name, address, port, icon, color, timezone, allowedSecurityLevel, population FROM realmlist WHERE color <> 3 ORDER BY name" ); + //// 0 1 2 3 4 5 6 7 8 9 + QueryResult *result = loginDatabase.Query( "SELECT id, name, address, port, icon, color, timezone, allowedSecurityLevel, population, realmbuilds FROM realmlist WHERE color <> 3 ORDER BY name" ); ///- Circle through results and add them to the realm map if(result) @@ -96,9 +108,9 @@ void RealmList::UpdateRealms(bool init) uint8 allowedSecurityLevel = fields[7].GetUInt8(); - UpdateRealm(fields[0].GetUInt32(), fields[1].GetCppString(),fields[2].GetCppString(),fields[3].GetUInt32(),fields[4].GetUInt8(), fields[5].GetUInt8(), fields[6].GetUInt8(), (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), fields[8].GetFloat() ); + UpdateRealm(fields[0].GetUInt32(), fields[1].GetCppString(),fields[2].GetCppString(),fields[3].GetUInt32(),fields[4].GetUInt8(), fields[5].GetUInt8(), fields[6].GetUInt8(), (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), fields[8].GetFloat(), fields[9].GetString() ); if(init) - sLog.outString("Added realm \"%s\".", fields[1].GetString()); + sLog.outString("Added realm \"%s\"", fields[1].GetString()); } while( result->NextRow() ); delete result; } diff --git a/src/realmd/RealmList.h b/src/realmd/RealmList.h index 936f83e4e..a127ed68d 100644 --- a/src/realmd/RealmList.h +++ b/src/realmd/RealmList.h @@ -35,6 +35,7 @@ struct Realm uint32 m_ID; AccountTypes allowedSecurityLevel; float populationLevel; + std::set realmbuilds; }; /// Storage object for the list of realms on the server @@ -57,7 +58,7 @@ class RealmList uint32 size() const { return m_realms.size(); } private: void UpdateRealms(bool init); - void UpdateRealm( uint32 ID, const std::string& name, const std::string& address, uint32 port, uint8 icon, uint8 color, uint8 timezone, AccountTypes allowedSecurityLevel, float popu); + void UpdateRealm( uint32 ID, const std::string& name, const std::string& address, uint32 port, uint8 icon, uint8 color, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, const char* builds); private: RealmMap m_realms; ///< Internal map of realms uint32 m_UpdateInterval; diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index f9a41d104..20a391ce4 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9009" + #define REVISION_NR "9010" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index 01c496089..4a288f87f 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -2,5 +2,5 @@ #define __REVISION_SQL_H__ #define REVISION_DB_CHARACTERS "required_8874_01_characters_character_skills" #define REVISION_DB_MANGOS "required_9007_01_mangos_spell_proc_event" - #define REVISION_DB_REALMD "required_8728_01_realmd_account" + #define REVISION_DB_REALMD "required_9010_01_realmd_realmlist" #endif // __REVISION_SQL_H__