aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/authserver/Authentication/AuthCodes.cpp5
-rw-r--r--src/server/authserver/Authentication/AuthCodes.h91
-rw-r--r--src/server/authserver/Main.cpp12
-rw-r--r--src/server/authserver/Realms/RealmList.cpp43
-rw-r--r--src/server/authserver/Realms/RealmList.h12
-rw-r--r--src/server/authserver/Server/AuthSocket.cpp51
-rw-r--r--src/server/authserver/Server/AuthSocket.h2
-rw-r--r--src/server/authserver/Server/BattlenetBitStream.h220
-rw-r--r--src/server/authserver/Server/BattlenetManager.cpp96
-rw-r--r--src/server/authserver/Server/BattlenetManager.h111
-rw-r--r--src/server/authserver/Server/BattlenetPacketCrypt.cpp42
-rw-r--r--src/server/authserver/Server/BattlenetPacketCrypt.h36
-rw-r--r--src/server/authserver/Server/BattlenetPackets.cpp449
-rw-r--r--src/server/authserver/Server/BattlenetPackets.h360
-rw-r--r--src/server/authserver/Server/BattlenetSocket.cpp1011
-rw-r--r--src/server/authserver/Server/BattlenetSocket.h120
-rw-r--r--src/server/authserver/Server/RealmAcceptor.h4
-rw-r--r--src/server/game/Accounts/AccountMgr.cpp81
-rw-r--r--src/server/game/Accounts/AccountMgr.h6
-rw-r--r--src/server/game/Accounts/BattlenetAccountMgr.cpp144
-rw-r--r--src/server/game/Accounts/BattlenetAccountMgr.h45
-rw-r--r--src/server/game/Server/WorldSocket.cpp17
-rw-r--r--src/server/game/Server/WorldSocket.h4
-rw-r--r--src/server/game/Warden/WardenWin.cpp4
-rw-r--r--src/server/scripts/Commands/cs_account.cpp60
-rw-r--r--src/server/scripts/Commands/cs_ban.cpp6
-rw-r--r--src/server/scripts/Commands/cs_character.cpp2
-rw-r--r--src/server/scripts/Commands/cs_lookup.cpp3
-rw-r--r--src/server/scripts/Commands/cs_rbac.cpp2
-rw-r--r--src/server/shared/Cryptography/Authentication/PacketCrypt.cpp39
-rw-r--r--src/server/shared/Cryptography/Authentication/PacketCrypt.h (renamed from src/server/shared/Cryptography/Authentication/AuthCrypt.h)21
-rw-r--r--src/server/shared/Cryptography/Authentication/WorldPacketCrypt.cpp (renamed from src/server/shared/Cryptography/Authentication/AuthCrypt.cpp)38
-rw-r--r--src/server/shared/Cryptography/Authentication/WorldPacketCrypt.h34
-rw-r--r--src/server/shared/Cryptography/BigNumber.cpp14
-rw-r--r--src/server/shared/Cryptography/BigNumber.h5
-rw-r--r--src/server/shared/Cryptography/HMACSHA1.cpp57
-rw-r--r--src/server/shared/Cryptography/HmacHash.cpp66
-rw-r--r--src/server/shared/Cryptography/HmacHash.h (renamed from src/server/shared/Cryptography/HMACSHA1.h)22
-rw-r--r--src/server/shared/Cryptography/SHA256.cpp66
-rw-r--r--src/server/shared/Cryptography/SHA256.h49
-rw-r--r--src/server/shared/Database/Implementation/LoginDatabase.cpp21
-rw-r--r--src/server/shared/Database/Implementation/LoginDatabase.h18
-rw-r--r--src/server/shared/Utilities/Util.cpp36
-rw-r--r--src/server/shared/Utilities/Util.h2
-rw-r--r--src/server/worldserver/RemoteAccess/RASocket.cpp6
45 files changed, 3276 insertions, 257 deletions
diff --git a/src/server/authserver/Authentication/AuthCodes.cpp b/src/server/authserver/Authentication/AuthCodes.cpp
index 55517884b8e..44921f6a1cd 100644
--- a/src/server/authserver/Authentication/AuthCodes.cpp
+++ b/src/server/authserver/Authentication/AuthCodes.cpp
@@ -79,4 +79,9 @@ namespace AuthHelper
return NULL;
}
+
+ bool IsBuildSupportingBattlenet(int build)
+ {
+ return build >= 15595;
+ }
}
diff --git a/src/server/authserver/Authentication/AuthCodes.h b/src/server/authserver/Authentication/AuthCodes.h
index 97b4779da0e..eb9f443f624 100644
--- a/src/server/authserver/Authentication/AuthCodes.h
+++ b/src/server/authserver/Authentication/AuthCodes.h
@@ -70,6 +70,96 @@ enum LoginResult
LOGIN_LOCKED_ENFORCED = 0x10
};
+enum GameAccountFlags
+{
+ GAMEACCOUNT_FLAG_GM = 0x00000001,
+ GAMEACCOUNT_FLAG_NOKICK = 0x00000002,
+ GAMEACCOUNT_FLAG_COLLECTOR = 0x00000004,
+ GAMEACCOUNT_FLAG_WOW_TRIAL = 0x00000008,
+ GAMEACCOUNT_FLAG_CANCELLED = 0x00000010,
+ GAMEACCOUNT_FLAG_IGR = 0x00000020,
+ GAMEACCOUNT_FLAG_WHOLESALER = 0x00000040,
+ GAMEACCOUNT_FLAG_PRIVILEGED = 0x00000080,
+ GAMEACCOUNT_FLAG_EU_FORBID_ELV = 0x00000100,
+ GAMEACCOUNT_FLAG_EU_FORBID_BILLING = 0x00000200,
+ GAMEACCOUNT_FLAG_WOW_RESTRICTED = 0x00000400,
+ GAMEACCOUNT_FLAG_REFERRAL = 0x00000800,
+ GAMEACCOUNT_FLAG_BLIZZARD = 0x00001000,
+ GAMEACCOUNT_FLAG_RECURRING_BILLING = 0x00002000,
+ GAMEACCOUNT_FLAG_NOELECTUP = 0x00004000,
+ GAMEACCOUNT_FLAG_KR_CERTIFICATE = 0x00008000,
+ GAMEACCOUNT_FLAG_EXPANSION_COLLECTOR = 0x00010000,
+ GAMEACCOUNT_FLAG_DISABLE_VOICE = 0x00020000,
+ GAMEACCOUNT_FLAG_DISABLE_VOICE_SPEAK = 0x00040000,
+ GAMEACCOUNT_FLAG_REFERRAL_RESURRECT = 0x00080000,
+ GAMEACCOUNT_FLAG_EU_FORBID_CC = 0x00100000,
+ GAMEACCOUNT_FLAG_OPENBETA_DELL = 0x00200000,
+ GAMEACCOUNT_FLAG_PROPASS = 0x00400000,
+ GAMEACCOUNT_FLAG_PROPASS_LOCK = 0x00800000,
+ GAMEACCOUNT_FLAG_PENDING_UPGRADE = 0x01000000,
+ GAMEACCOUNT_FLAG_RETAIL_FROM_TRIAL = 0x02000000,
+ GAMEACCOUNT_FLAG_EXPANSION2_COLLECTOR = 0x04000000,
+ GAMEACCOUNT_FLAG_OVERMIND_LINKED = 0x08000000,
+ GAMEACCOUNT_FLAG_DEMOS = 0x10000000,
+ GAMEACCOUNT_FLAG_DEATH_KNIGHT_OK = 0x20000000,
+};
+
+namespace Battlenet
+{
+ enum AuthResult
+ {
+ AUTH_OK = 0,
+ AUTH_INTERNAL_ERROR = 100,
+ AUTH_CORRUPTED_MODULE = 102,
+ AUTH_BAD_SERVER_PROOF = 103,
+ AUTH_UNKNOWN_ACCOUNT = 104,
+ AUTH_CLOSED = 105,
+ AUTH_LOGIN_TIMEOUT = 106,
+ AUTH_NO_GAME_ACCOUNTS = 107,
+ AUTH_INVALID_TOKEN = 108,
+ AUTH_INVALID_PROGRAM = 109,
+ AUTH_INVALID_OS = 110,
+ AUTH_UNSUPPORTED_LANGUAGE = 111,
+ AUTH_REGION_BAD_VERSION = 112,
+ AUTH_TEMP_OUTAGE = 113,
+ AUTH_CANT_DOWNLOAD_MODULE = 114,
+ AUTH_DUPLICATE_LOGON = 115,
+ AUTH_BAD_CREDENTIALS_2 = 116,
+ AUTH_VERSION_CHECK_SUCCEEDED = 117,
+ AUTH_BAD_VERSION_HASH = 118,
+ AUTH_CANT_RETRIEVE_PORTAL_LIST = 119,
+ AUTH_DARK_PORTAL_DOES_NOT_EXIST = 120,
+ AUTH_DARK_PORTAL_FILE_CORRUPTED = 121,
+ AUTH_BATTLENET_MAINTENANCE = 122,
+ AUTH_LOGON_TOO_FAST = 123,
+ AUTH_USE_GRUNT_LOGON = 124,
+ AUTH_NO_GAME_ACCOUNTS_IN_REGION = 140,
+ AUTH_ACCOUNT_LOCKED = 141,
+
+ LOGIN_SERVER_BUSY = 200,
+ LOGIN_NO_GAME_ACCOUNT = 201,
+ LOGIN_BANNED = 202,
+ LOGIN_SUSPENDED = 203,
+ LOGIN_GAME_ACCOUNT_LOCKED = 204,
+ LOGIN_ALREADY_ONLINE = 205,
+ LOGIN_NOTIME = 206,
+ LOGIN_EXPIRED = 207,
+ LOGIN_EXPIRED_2 = 208,
+ LOGIN_PARENTALCONTROL = 209,
+ LOGIN_TRIAL_EXPIRED = 210,
+ LOGIN_ANTI_INDULGENCE = 211,
+ LOGIN_INCORRECT_REGION = 212,
+ LOGIN_LOCKED_ENFORCED = 213,
+ LOGIN_CHARGEBACK = 214,
+ LOGIN_IGR_WITHOUT_BNET = 215,
+ LOGIN_UNLOCKABLE_LOCK = 216,
+ LOGIN_IGR_REQUIRED = 217,
+ LOGIN_PAYMENT_CHANGED = 218,
+ LOGIN_INVALID_PAYMENT = 219,
+ LOGIN_INVALID_ACCOUNT_STATE = 220
+ };
+}
+
enum ExpansionFlags
{
POST_BC_EXP_FLAG = 0x2,
@@ -92,6 +182,7 @@ namespace AuthHelper
bool IsAcceptedClientBuild(int build);
bool IsPostBCAcceptedClientBuild(int build);
bool IsPreBCAcceptedClientBuild(int build);
+ bool IsBuildSupportingBattlenet(int build);
}
#endif
diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp
index be6623c7fa3..be2e6a11d4f 100644
--- a/src/server/authserver/Main.cpp
+++ b/src/server/authserver/Main.cpp
@@ -151,7 +151,8 @@ extern int main(int argc, char** argv)
}
// Launch the listening network socket
- RealmAcceptor acceptor;
+ RealmAcceptor<AuthSocket> acceptor;
+ RealmAcceptor<Battlenet::Socket> bnetacceptor;
int32 rmport = sConfigMgr->GetIntDefault("RealmServerPort", 3724);
if (rmport < 0 || rmport > 0xFFFF)
@@ -170,6 +171,13 @@ extern int main(int argc, char** argv)
return 1;
}
+ bind_addr.set_port_number(1119);
+ if (bnetacceptor.open(bind_addr, ACE_Reactor::instance(), ACE_NONBLOCK) == -1)
+ {
+ TC_LOG_ERROR("server.authserver", "Auth server can not bind to %s:%d", bind_ip.c_str(), 1119);
+ return 1;
+ }
+
// Initialize the signal handlers
AuthServerSignalHandler SignalINT, SignalTERM;
@@ -246,6 +254,8 @@ extern int main(int argc, char** argv)
#endif
#endif
+ sBattlenetMgr->Load();
+
// maximum counter for next ping
uint32 numLoops = (sConfigMgr->GetIntDefault("MaxPingTime", 30) * (MINUTE * 1000000 / 100000));
uint32 loopCounter = 0;
diff --git a/src/server/authserver/Realms/RealmList.cpp b/src/server/authserver/Realms/RealmList.cpp
index 4aeecfc0aaa..48b7a178c2d 100644
--- a/src/server/authserver/Realms/RealmList.cpp
+++ b/src/server/authserver/Realms/RealmList.cpp
@@ -18,9 +18,29 @@
#include "Common.h"
#include "RealmList.h"
+#include "BattlenetManager.h"
#include "Database/DatabaseEnv.h"
+#include "Util.h"
-RealmList::RealmList() : m_UpdateInterval(0), m_NextUpdateTime(time(NULL)) { }
+ACE_INET_Addr const& Realm::GetAddressForClient(ACE_INET_Addr const& clientAddr) const
+{
+ // Attempt to send best address for client
+ if (clientAddr.is_loopback())
+ // Assume that user connecting from the machine that authserver is located on
+ // has all realms available in his local network
+ return LocalAddress;
+
+ // Check if connecting client is in the same network
+ if (IsIPAddrInNetwork(LocalAddress, clientAddr, LocalSubnetMask))
+ return LocalAddress;
+
+ // Return external IP
+ return ExternalAddress;
+}
+
+RealmList::RealmList() : m_UpdateInterval(0), m_NextUpdateTime(time(NULL))
+{
+}
// Load the realm list from the database
void RealmList::Initialize(uint32 updateInterval)
@@ -31,7 +51,7 @@ void RealmList::Initialize(uint32 updateInterval)
UpdateRealms(true);
}
-void RealmList::UpdateRealm(uint32 id, const std::string& name, ACE_INET_Addr const& address, ACE_INET_Addr const& localAddr, ACE_INET_Addr const& localSubmask, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, uint32 build)
+void RealmList::UpdateRealm(uint32 id, const std::string& name, ACE_INET_Addr const& address, ACE_INET_Addr const& localAddr, ACE_INET_Addr const& localSubmask, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, uint32 build, uint8 region, uint8 battlegroup)
{
// Create new if not exist or update existed
Realm& realm = m_realms[name];
@@ -49,6 +69,8 @@ void RealmList::UpdateRealm(uint32 id, const std::string& name, ACE_INET_Addr co
realm.LocalAddress = localAddr;
realm.LocalSubnetMask = localSubmask;
realm.gamebuild = build;
+ realm.Region = region;
+ realm.Battlegroup = battlegroup;
}
void RealmList::UpdateIfNeed()
@@ -91,12 +113,14 @@ void RealmList::UpdateRealms(bool init)
uint8 allowedSecurityLevel = fields[9].GetUInt8();
float pop = fields[10].GetFloat();
uint32 build = fields[11].GetUInt32();
+ uint8 region = fields[12].GetUInt8();
+ uint8 battlegroup = fields[13].GetUInt8();
ACE_INET_Addr externalAddr(port, externalAddress.c_str(), AF_INET);
ACE_INET_Addr localAddr(port, localAddress.c_str(), AF_INET);
ACE_INET_Addr submask(0, localSubmask.c_str(), AF_INET);
- UpdateRealm(realmId, name, externalAddr, localAddr, submask, icon, flag, timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop, build);
+ UpdateRealm(realmId, name, externalAddr, localAddr, submask, icon, flag, timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop, build, region, battlegroup);
if (init)
TC_LOG_INFO("server.authserver", "Added realm \"%s\" at %s:%u.", name.c_str(), m_realms[name].ExternalAddress.get_host_addr(), port);
@@ -104,3 +128,16 @@ void RealmList::UpdateRealms(bool init)
while (result->NextRow());
}
}
+
+Realm const* RealmList::GetRealm(Battlenet::RealmId const& id) const
+{
+ auto itr = std::find_if(m_realms.begin(), m_realms.end(), [id](RealmMap::value_type const& pair)
+ {
+ return pair.second.Region == id.Region && pair.second.Battlegroup == id.Battlegroup && pair.second.m_ID == id.Index;
+ });
+
+ if (itr != m_realms.end())
+ return &itr->second;
+
+ return NULL;
+}
diff --git a/src/server/authserver/Realms/RealmList.h b/src/server/authserver/Realms/RealmList.h
index 1d76c39e4f0..c4a6b4eaa0b 100644
--- a/src/server/authserver/Realms/RealmList.h
+++ b/src/server/authserver/Realms/RealmList.h
@@ -51,8 +51,17 @@ struct Realm
AccountTypes allowedSecurityLevel;
float populationLevel;
uint32 gamebuild;
+ uint8 Region;
+ uint8 Battlegroup;
+
+ ACE_INET_Addr const& GetAddressForClient(ACE_INET_Addr const& clientAddr) const;
};
+namespace Battlenet
+{
+ struct RealmId;
+}
+
/// Storage object for the list of realms on the server
class RealmList
{
@@ -71,10 +80,11 @@ public:
RealmMap::const_iterator begin() const { return m_realms.begin(); }
RealmMap::const_iterator end() const { return m_realms.end(); }
uint32 size() const { return m_realms.size(); }
+ Realm const* GetRealm(Battlenet::RealmId const& id) const;
private:
void UpdateRealms(bool init=false);
- void UpdateRealm(uint32 id, const std::string& name, ACE_INET_Addr const& address, ACE_INET_Addr const& localAddr, ACE_INET_Addr const& localSubmask, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, uint32 build);
+ void UpdateRealm(uint32 id, const std::string& name, ACE_INET_Addr const& address, ACE_INET_Addr const& localAddr, ACE_INET_Addr const& localSubmask, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, uint32 build, uint8 region, uint8 battlegroup);
RealmMap m_realms;
uint32 m_UpdateInterval;
diff --git a/src/server/authserver/Server/AuthSocket.cpp b/src/server/authserver/Server/AuthSocket.cpp
index c7bb600024a..7ca49b03b56 100644
--- a/src/server/authserver/Server/AuthSocket.cpp
+++ b/src/server/authserver/Server/AuthSocket.cpp
@@ -293,19 +293,11 @@ void AuthSocket::_SetVSFields(const std::string& rI)
x.SetBinary(sha.GetDigest(), sha.GetLength());
v = g.ModExp(x, N);
- // No SQL injection (username escaped)
- char *v_hex, *s_hex;
- v_hex = v.AsHexStr();
- s_hex = s.AsHexStr();
-
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_VS);
- stmt->setString(0, v_hex);
- stmt->setString(1, s_hex);
+ stmt->setString(0, v.AsHexStr());
+ stmt->setString(1, s.AsHexStr());
stmt->setString(2, _login);
LoginDatabase.Execute(stmt);
-
- OPENSSL_free(v_hex);
- OPENSSL_free(s_hex);
}
// Logon Challenge command handler
@@ -488,7 +480,9 @@ bool AuthSocket::_HandleLogonChallenge()
unk3.SetRand(16 * 8);
// Fill the response packet with the result
- if (AuthHelper::IsAcceptedClientBuild(_build))
+ if (fields[9].GetUInt32() && AuthHelper::IsBuildSupportingBattlenet(_build))
+ pkt << uint8(WOW_FAIL_USE_BATTLENET);
+ else if (AuthHelper::IsAcceptedClientBuild(_build))
pkt << uint8(WOW_SUCCESS);
else
pkt << uint8(WOW_FAIL_VERSION_INVALID);
@@ -650,19 +644,14 @@ bool AuthSocket::_HandleLogonProof()
TC_LOG_DEBUG("server.authserver", "'%s:%d' User '%s' successfully authenticated", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str());
// Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account
- // No SQL injection (escaped user name) and IP address as received by socket
- const char *K_hex = K.AsHexStr();
-
PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF);
- stmt->setString(0, K_hex);
+ stmt->setString(0, K.AsHexStr());
stmt->setString(1, socket().getRemoteAddress().c_str());
stmt->setUInt32(2, GetLocaleByName(_localizationName));
stmt->setString(3, _os);
stmt->setString(4, _login);
LoginDatabase.DirectExecute(stmt);
- OPENSSL_free((void*)K_hex);
-
// Finish SRP6 and send the final result to the client
sha.Initialize();
sha.UpdateBigNumbers(&A, &M, &K, NULL);
@@ -693,7 +682,7 @@ bool AuthSocket::_HandleLogonProof()
memcpy(proof.M2, sha.GetDigest(), 20);
proof.cmd = AUTH_LOGON_PROOF;
proof.error = 0;
- proof.unk1 = 0x00800000; // Accountflags. 0x01 = GM, 0x08 = Trial, 0x00800000 = Pro pass (arena tournament)
+ proof.unk1 = GAMEACCOUNT_FLAG_PROPASS_LOCK;
proof.unk2 = 0x00; // SurveyId
proof.unk3 = 0x00;
socket().send((char *)&proof, sizeof(proof));
@@ -879,28 +868,6 @@ bool AuthSocket::_HandleReconnectProof()
}
}
-ACE_INET_Addr const& AuthSocket::GetAddressForClient(Realm const& realm, ACE_INET_Addr const& clientAddr)
-{
- // Attempt to send best address for client
- if (clientAddr.is_loopback())
- {
- // Try guessing if realm is also connected locally
- if (realm.LocalAddress.is_loopback() || realm.ExternalAddress.is_loopback())
- return clientAddr;
-
- // Assume that user connecting from the machine that authserver is located on
- // has all realms available in his local network
- return realm.LocalAddress;
- }
-
- // Check if connecting client is in the same network
- if (IsIPAddrInNetwork(realm.LocalAddress, clientAddr, realm.LocalSubnetMask))
- return realm.LocalAddress;
-
- // Return external IP
- return realm.ExternalAddress;
-}
-
// Realm List command handler
bool AuthSocket::_HandleRealmList()
{
@@ -981,12 +948,12 @@ bool AuthSocket::_HandleRealmList()
pkt << lock; // if 1, then realm locked
pkt << uint8(flag); // RealmFlags
pkt << name;
- pkt << GetAddressString(GetAddressForClient(realm, clientAddr));
+ pkt << GetAddressString(realm.GetAddressForClient(clientAddr));
pkt << realm.populationLevel;
pkt << AmountOfCharacters;
pkt << realm.timezone; // realm category
if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
- pkt << uint8(0x2C); // unk, may be realm number/id?
+ pkt << uint8(realm.m_ID);
else
pkt << uint8(0x0); // 1.12.1 and 1.12.2 clients
diff --git a/src/server/authserver/Server/AuthSocket.h b/src/server/authserver/Server/AuthSocket.h
index 5e04d459ba1..e81944389ef 100644
--- a/src/server/authserver/Server/AuthSocket.h
+++ b/src/server/authserver/Server/AuthSocket.h
@@ -39,8 +39,6 @@ public:
virtual void OnAccept(void);
virtual void OnClose(void);
- static ACE_INET_Addr const& GetAddressForClient(Realm const& realm, ACE_INET_Addr const& clientAddr);
-
bool _HandleLogonChallenge();
bool _HandleLogonProof();
bool _HandleReconnectChallenge();
diff --git a/src/server/authserver/Server/BattlenetBitStream.h b/src/server/authserver/Server/BattlenetBitStream.h
new file mode 100644
index 00000000000..a59150f5327
--- /dev/null
+++ b/src/server/authserver/Server/BattlenetBitStream.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __BATTLENETBITSTREAM_H__
+#define __BATTLENETBITSTREAM_H__
+
+#include "ByteConverter.h"
+#include <exception>
+#include <string>
+#include <vector>
+#include <type_traits>
+#include <ace/Auto_Ptr.h>
+#include <ace/Stack_Trace.h>
+
+namespace Battlenet
+{
+ class BitStreamPositionException : public std::exception
+ {
+ public:
+ BitStreamPositionException() : st(1) { }
+
+ char const* what() const
+ {
+ return st.c_str();
+ }
+
+ ACE_Stack_Trace st;
+ };
+
+ class BitStream
+ {
+ public:
+ static uint32 const MaxSize = 0x1000;
+
+ // length : The maximum number of bytes to read
+ BitStream(uint32 length) : _numBits(length * 8), _readPos(0), _writePos(0)
+ {
+ _buffer.resize(length, 0);
+ }
+
+ BitStream() : _numBits(0), _readPos(0), _writePos(0)
+ {
+ _buffer.reserve(0x1000);
+ }
+
+ void AlignToNextByte()
+ {
+ _readPos = (_readPos + 7) & ~7;
+ _writePos = (_writePos + 7) & ~7;
+ }
+
+ std::string ReadString(uint32 bitCount, int32 baseLength = 0)
+ {
+ uint32 len = Read<uint32>(bitCount) + baseLength;
+ AlignToNextByte();
+ std::string str(reinterpret_cast<char*>(&_buffer[_readPos >> 3]), len);
+ _readPos += len * 8;
+ return str;
+ }
+
+ ACE_Auto_Array_Ptr<uint8> ReadBytes(uint32 count)
+ {
+ AlignToNextByte();
+ if (_readPos + count * 8 > _numBits)
+ throw BitStreamPositionException();
+
+ ACE_Auto_Array_Ptr<uint8> buf(new uint8[count]);
+ memcpy(buf.get(), &_buffer[_readPos >> 3], count);
+ _readPos += count * 8;
+ return buf;
+ }
+
+ float ReadFloat()
+ {
+ uint32 val = Read<uint32>(32);
+ return *reinterpret_cast<float*>(&val);
+ }
+
+ std::string ReadFourCC()
+ {
+ uint32 fcc = Read<uint32>(32);
+ EndianConvertReverse(fcc);
+ size_t len = 4;
+ while (!(fcc & 0xFF))
+ {
+ fcc >>= 8;
+ --len;
+ }
+
+ return std::string(reinterpret_cast<char*>(&fcc), len);
+ }
+
+ template<typename T>
+ T Read(uint32 bitCount)
+ {
+ static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "T must be an integer type");
+
+ uint64 ret = 0;
+ while (bitCount != 0)
+ {
+ uint32 bitPos = (_readPos & 7);
+ uint32 bitsLeftInByte = 8 - bitPos;
+ if (bitsLeftInByte >= bitCount)
+ bitsLeftInByte = bitCount;
+
+ bitCount -= bitsLeftInByte;
+ ret |= (uint64)(_buffer[_readPos >> 3] >> bitPos & (uint32)((uint8)(1 << bitsLeftInByte) - 1)) << bitCount;
+ _readPos += bitsLeftInByte;
+ }
+
+ return static_cast<T>(ret);
+ }
+
+ void WriteString(std::string const& str, uint32 bitCount, int32 baseLength = 0)
+ {
+ Write(str.length() + baseLength, bitCount);
+ WriteBytes(str.c_str(), str.length());
+ }
+
+ template<typename T>
+ void WriteBytes(T* data, uint32 count)
+ {
+ AlignToNextByte();
+ if (!count || !data)
+ return;
+
+ if ((_writePos >> 3) + count > MaxSize)
+ throw BitStreamPositionException();
+
+ _buffer.resize(_buffer.size() + count);
+ memcpy(&_buffer[_writePos >> 3], data, count);
+ _writePos += count * 8;
+ }
+
+ void WriteFloat(float value)
+ {
+ uint32 intVal = *reinterpret_cast<uint32*>(&value);
+ Write(intVal, 32);
+ }
+
+ void WriteFourCC(std::string const& fcc)
+ {
+ uint32 intVal = *(uint32*)fcc.c_str();
+ size_t len = fcc.length();
+ EndianConvertReverse(intVal);
+ // Add padding
+ while (len++ < 4)
+ intVal >>= 8;
+
+ Write(intVal, 32);
+ }
+
+ template<typename T>
+ void Write(T value, uint32 bitCount)
+ {
+ static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "T must be an integer type");
+
+ if (_writePos + bitCount >= 8 * MaxSize)
+ throw BitStreamPositionException();
+
+ while (bitCount != 0)
+ {
+ uint32 bitPos = (_writePos & 7);
+ uint32 bitsLeftInByte = 8 - bitPos;
+ if (bitsLeftInByte >= bitCount)
+ bitsLeftInByte = bitCount;
+
+ bitCount -= bitsLeftInByte;
+
+ uint8 firstHalf = (uint8)(~(((uint8)(1 << bitsLeftInByte) - 1) << bitPos));
+ uint8 secondHalf = (uint8)((((uint8)(1 << bitsLeftInByte) - 1) & (uint8)(value >> bitCount)) << bitPos);
+
+ if (_buffer.size() > (_writePos >> 3))
+ _buffer[_writePos >> 3] = (uint8)(_buffer[_writePos >> 3] & firstHalf | secondHalf);
+ else
+ _buffer.push_back(secondHalf);
+
+ _writePos += bitsLeftInByte;
+ }
+ }
+
+ void SetReadPos(uint32 bits)
+ {
+ if (bits >= _numBits)
+ throw BitStreamPositionException();
+
+ _readPos = bits;
+ }
+
+ bool IsRead() const { return _readPos >= _numBits; }
+
+ uint8* GetBuffer() { return _buffer.data(); }
+
+ size_t GetSize() const { return _buffer.size(); }
+
+ void FinishReading() { _readPos = _numBits; }
+
+ private:
+ std::vector<uint8> _buffer;
+ uint32 _numBits;
+ uint32 _readPos;
+ uint32 _writePos;
+ };
+}
+
+#endif // __BATTLENETBITSTREAM_H__
diff --git a/src/server/authserver/Server/BattlenetManager.cpp b/src/server/authserver/Server/BattlenetManager.cpp
new file mode 100644
index 00000000000..f470c365b56
--- /dev/null
+++ b/src/server/authserver/Server/BattlenetManager.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "BattlenetManager.h"
+#include "DatabaseEnv.h"
+
+BattlenetMgr::~BattlenetMgr()
+{
+ for (Battlenet::Component* component : _components)
+ delete component;
+
+ for (auto const& m : _modules)
+ delete m.second;
+}
+
+void BattlenetMgr::Load()
+{
+ LoadComponents();
+ LoadModules();
+}
+
+void BattlenetMgr::LoadComponents()
+{
+ QueryResult result = LoginDatabase.Query("SELECT Program, Platform, Build FROM battlenet_components");
+ if (result)
+ {
+ do
+ {
+ Field* fields = result->Fetch();
+ Battlenet::Component* component = new Battlenet::Component();
+ component->Program = fields[0].GetString();
+ component->Platform = fields[1].GetString();
+ component->Build = fields[2].GetUInt32();
+
+ _components.insert(component);
+ _programs.insert(component->Program);
+ _platforms.insert(component->Platform);
+
+ } while (result->NextRow());
+ }
+}
+
+void BattlenetMgr::LoadModules()
+{
+ QueryResult result = LoginDatabase.Query("SELECT `Hash`, `Name`, `Type`, `System`, `Data` FROM battlenet_modules");
+ if (result)
+ {
+ do
+ {
+ Field* fields = result->Fetch();
+ Battlenet::ModuleInfo* module = new Battlenet::ModuleInfo();
+ module->Type = fields[2].GetString();
+ HexStrToByteArray(fields[0].GetString(), module->ModuleId);
+ std::string data = fields[4].GetString();
+ module->DataSize = data.length() / 2;
+ if (module->DataSize)
+ {
+ module->Data = new uint8[data.length() / 2];
+ HexStrToByteArray(data, module->Data);
+ }
+
+ _modules[{ fields[3].GetString(), fields[1].GetString() }] = module;
+ } while (result->NextRow());
+ }
+}
+
+bool BattlenetMgr::HasComponent(Battlenet::Component const* component) const
+{
+ for (Battlenet::Component const* c : _components)
+ if (component->Program == c->Program && component->Platform == c->Platform && component->Build == c->Build)
+ return true;
+
+ return false;
+}
+
+Battlenet::ModuleInfo* BattlenetMgr::CreateModule(std::string const& os, std::string const& name) const
+{
+ Battlenet::ModuleKey key { os, name };
+ ASSERT(_modules.count(key));
+
+ return new Battlenet::ModuleInfo(*_modules.at(key));
+}
diff --git a/src/server/authserver/Server/BattlenetManager.h b/src/server/authserver/Server/BattlenetManager.h
new file mode 100644
index 00000000000..3b8a145f4e9
--- /dev/null
+++ b/src/server/authserver/Server/BattlenetManager.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __BATTLENETMANAGER_H__
+#define __BATTLENETMANAGER_H__
+
+#include "Define.h"
+#include <ace/Singleton.h>
+#include <string>
+#include <set>
+#include <map>
+
+namespace Battlenet
+{
+ struct Component
+ {
+ std::string Program;
+ std::string Platform;
+ uint32 Build;
+ };
+
+ struct ModuleKey
+ {
+ std::string Platform;
+ std::string Name;
+
+ bool operator<(ModuleKey const& right) const
+ {
+ int32 res = Platform.compare(right.Platform);
+ if (res < 0)
+ return true;
+ else if (res > 0)
+ return false;
+
+ return Name < right.Name;
+ }
+ };
+
+ struct ModuleInfo
+ {
+ ModuleInfo() : Region("EU"), DataSize(0), Data(nullptr) { }
+ ModuleInfo(ModuleInfo const& right) : Type(right.Type), Region(right.Region), DataSize(right.DataSize), Data(nullptr)
+ {
+ memcpy(ModuleId, right.ModuleId, 32);
+ if (DataSize)
+ {
+ Data = new uint8[DataSize];
+ memcpy(Data, right.Data, DataSize);
+ }
+ }
+ ~ModuleInfo()
+ {
+ delete Data;
+ }
+
+ std::string Type;
+ std::string Region;
+ uint8 ModuleId[32];
+ uint32 DataSize;
+ uint8* Data;
+ };
+
+ struct RealmId
+ {
+ uint8 Region;
+ uint8 Battlegroup;
+ uint32 Index;
+ uint32 Build;
+ };
+}
+
+class BattlenetMgr
+{
+ friend class ACE_Singleton<BattlenetMgr, ACE_Null_Mutex>;
+ BattlenetMgr() { }
+ ~BattlenetMgr();
+
+public:
+ void Load();
+ bool HasComponent(Battlenet::Component const* component) const;
+ bool HasProgram(std::string const& program) const { return _programs.count(program); }
+ bool HasPlatform(std::string const& platform) const { return _platforms.count(platform); }
+ Battlenet::ModuleInfo* CreateModule(std::string const& os, std::string const& name) const;
+
+private:
+ void LoadComponents();
+ void LoadModules();
+
+ std::set<Battlenet::Component*> _components;
+ std::set<std::string> _programs;
+ std::set<std::string> _platforms;
+ std::map<Battlenet::ModuleKey, Battlenet::ModuleInfo*> _modules;
+};
+
+#define sBattlenetMgr ACE_Singleton<BattlenetMgr, ACE_Null_Mutex>::instance()
+
+#endif // __BATTLENETMANAGER_H__
diff --git a/src/server/authserver/Server/BattlenetPacketCrypt.cpp b/src/server/authserver/Server/BattlenetPacketCrypt.cpp
new file mode 100644
index 00000000000..de4cf73f71c
--- /dev/null
+++ b/src/server/authserver/Server/BattlenetPacketCrypt.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "BattlenetPacketCrypt.h"
+#include "Cryptography/HmacHash.h"
+#include "Cryptography/BigNumber.h"
+
+Battlenet::PacketCrypt::PacketCrypt() : ::PacketCrypt(SHA256_DIGEST_LENGTH)
+{
+}
+
+void Battlenet::PacketCrypt::Init(BigNumber* K)
+{
+ uint8 ServerEncryptionKey[SEED_KEY_SIZE] = { 0x68, 0xE0, 0xC7, 0x2E, 0xDD, 0xD6, 0xD2, 0xF3, 0x1E, 0x5A, 0xB1, 0x55, 0xB1, 0x8B, 0x63, 0x1E };
+ uint8 ClientDecryptionKey[SEED_KEY_SIZE] = { 0xDE, 0xA9, 0x65, 0xAE, 0x54, 0x3A, 0x1E, 0x93, 0x9E, 0x69, 0x0C, 0xAA, 0x68, 0xDE, 0x78, 0x39 };
+
+ HmacSha256 serverEncryptHmac(K->GetNumBytes(), K->AsByteArray().get());
+ serverEncryptHmac.UpdateData(ServerEncryptionKey, SEED_KEY_SIZE);
+ serverEncryptHmac.Finalize();
+
+ HmacSha256 clientDecryptHmac(K->GetNumBytes(), K->AsByteArray().get());
+ clientDecryptHmac.UpdateData(ClientDecryptionKey, SEED_KEY_SIZE);
+ clientDecryptHmac.Finalize();
+
+ _clientDecrypt.Init(clientDecryptHmac.GetDigest());
+ _serverEncrypt.Init(serverEncryptHmac.GetDigest());
+ _initialized = true;
+}
diff --git a/src/server/authserver/Server/BattlenetPacketCrypt.h b/src/server/authserver/Server/BattlenetPacketCrypt.h
new file mode 100644
index 00000000000..dde687651d3
--- /dev/null
+++ b/src/server/authserver/Server/BattlenetPacketCrypt.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __BATTLENETPACKETCRYPT_H__
+#define __BATTLENETPACKETCRYPT_H__
+
+#include "PacketCrypt.h"
+
+class BigNumber;
+
+namespace Battlenet
+{
+ class PacketCrypt : public ::PacketCrypt
+ {
+ public:
+ PacketCrypt();
+
+ void Init(BigNumber* K) override;
+ };
+}
+
+#endif // __BATTLENETPACKETCRYPT_H__
diff --git a/src/server/authserver/Server/BattlenetPackets.cpp b/src/server/authserver/Server/BattlenetPackets.cpp
new file mode 100644
index 00000000000..6a8e09090ef
--- /dev/null
+++ b/src/server/authserver/Server/BattlenetPackets.cpp
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "BattlenetPackets.h"
+#include "Common.h"
+#include "Util.h"
+#include <limits>
+#include <sstream>
+
+std::string Battlenet::PacketHeader::ToString() const
+{
+ std::ostringstream stream;
+ stream << "Battlenet::PacketHeader opcode: " << Opcode << ", channel: " << Channel;
+ return stream.str();
+}
+
+Battlenet::ServerPacket::ServerPacket(PacketHeader const& header) : Packet(header, *new BitStream())
+{
+ _stream.Write(header.Opcode, 6);
+ _stream.Write(1, 1);
+ _stream.Write(header.Channel, 4);
+}
+
+Battlenet::ServerPacket::~ServerPacket()
+{
+ delete &_stream;
+}
+
+void Battlenet::AuthChallenge::Read()
+{
+ Program = _stream.ReadFourCC();
+ Platform = _stream.ReadFourCC();
+ Locale = _stream.ReadFourCC();
+
+ Components.resize(_stream.Read<uint32>(6));
+ for (size_t i = 0; i < Components.size(); ++i)
+ {
+ Component& component = Components[i];
+ component.Program = _stream.ReadFourCC();
+ component.Platform = _stream.ReadFourCC();
+ component.Build = _stream.Read<uint32>(32);
+ }
+
+ if (_stream.Read<uint32>(1))
+ Login = _stream.ReadString(9, 3);
+}
+
+std::string Battlenet::AuthChallenge::ToString() const
+{
+ std::ostringstream stream;
+ stream << "Battlenet::AuthChallenge Program: " << Program << ", Platform: " << Platform << ", Locale: " << Locale;
+ for (Component const& component : Components)
+ stream << std::endl << "Battlenet::Component Program: " << component.Program << ", Platform: " << component.Platform << ", Build: " << component.Build;
+
+ if (!Login.empty())
+ stream << std::endl << "Battlenet::AuthChallenge Login: " << Login;
+
+ return stream.str();
+}
+
+void Battlenet::AuthResumeInfo::Read()
+{
+ Program = _stream.ReadFourCC();
+ Platform = _stream.ReadFourCC();
+ Locale = _stream.ReadFourCC();
+
+ Components.resize(_stream.Read<uint32>(6));
+ for (size_t i = 0; i < Components.size(); ++i)
+ {
+ Component& component = Components[i];
+ component.Program = _stream.ReadFourCC();
+ component.Platform = _stream.ReadFourCC();
+ component.Build = _stream.Read<uint32>(32);
+ }
+
+ Login = _stream.ReadString(9, 3);
+ Region = _stream.Read<uint8>(8);
+ GameAccountName = _stream.ReadString(5, 1);
+}
+
+std::string Battlenet::AuthResumeInfo::ToString() const
+{
+ std::ostringstream stream;
+ stream << "Battlenet::AuthReconnect Program: " << Program << ", Platform: " << Platform << ", Locale: " << Locale;
+ for (Component const& component : Components)
+ stream << std::endl << "Battlenet::Component Program: " << component.Program << ", Platform: " << component.Platform << ", Build: " << component.Build;
+
+ stream << std::endl << "Battlenet::AuthReconnect Login: " << Login;
+ stream << std::endl << "Battlenet::AuthReconnect Region: " << uint32(Region);
+ stream << std::endl << "Battlenet::AuthReconnect GameAccountName: " << GameAccountName;
+
+ return stream.str();
+}
+
+Battlenet::ProofRequest::~ProofRequest()
+{
+ for (size_t i = 0; i < Modules.size(); ++i)
+ delete Modules[i];
+}
+
+void Battlenet::ProofRequest::Write()
+{
+ _stream.Write(Modules.size(), 3);
+ for (ModuleInfo const* info : Modules)
+ {
+ _stream.WriteBytes(info->Type.c_str(), 4);
+ _stream.WriteFourCC(info->Region);
+ _stream.WriteBytes(info->ModuleId, 32);
+ _stream.Write(info->DataSize, 10);
+ _stream.WriteBytes(info->Data, info->DataSize);
+ }
+}
+
+std::string Battlenet::ProofRequest::ToString() const
+{
+ std::ostringstream stream;
+ stream << "Battlenet::ProofRequest modules " << Modules.size();
+ for (ModuleInfo const* module : Modules)
+ stream << std::endl << "Battlenet::ModuleInfo Locale " << module->Region.c_str() << ", ModuleId " << ByteArrayToHexStr(module->ModuleId, 32) << ", DataSize " << module->DataSize << ", Data " << ByteArrayToHexStr(module->Data, module->DataSize);
+
+ return stream.str();
+}
+
+Battlenet::ProofResponse::~ProofResponse()
+{
+ for (size_t i = 0; i < Modules.size(); ++i)
+ delete Modules[i];
+}
+
+void Battlenet::ProofResponse::Read()
+{
+ Modules.resize(_stream.Read<uint32>(3));
+ for (size_t i = 0; i < Modules.size(); ++i)
+ {
+ BitStream*& dataStream = Modules[i];
+ dataStream = new BitStream(_stream.Read<uint32>(10));
+ memcpy(dataStream->GetBuffer(), _stream.ReadBytes(dataStream->GetSize()).get(), dataStream->GetSize());
+ }
+}
+
+std::string Battlenet::ProofResponse::ToString() const
+{
+ std::ostringstream stream;
+ stream << "Battlenet::ProofResponse Modules " << Modules.size();
+ for (BitStream* module : Modules)
+ {
+ std::string hexStr = ByteArrayToHexStr(module->GetBuffer(), module->GetSize());
+ stream << std::endl << "Battlenet::ProofResponse::ModuleData Size: " << module->GetSize() << ", Data: " << hexStr;
+ }
+
+ return stream.str();
+}
+
+Battlenet::AuthComplete::~AuthComplete()
+{
+ for (ModuleInfo* m : Modules)
+ delete m;
+}
+
+void Battlenet::AuthComplete::Write()
+{
+ _stream.Write(Result != 0, 1);
+ if (Result == 0)
+ {
+ _stream.Write(Modules.size(), 3);
+ for (size_t i = 0; i < Modules.size(); ++i)
+ {
+ ModuleInfo* info = Modules[i];
+ _stream.WriteBytes(info->Type.c_str(), 4);
+ _stream.WriteFourCC(info->Region);
+ _stream.WriteBytes(info->ModuleId, 32);
+ _stream.Write(info->DataSize, 10);
+ _stream.WriteBytes(info->Data, info->DataSize);
+ }
+
+ _stream.Write(PingTimeout + std::numeric_limits<int32>::min(), 32);
+ _stream.Write(1, 1);
+ // if written == 1
+ {
+ _stream.Write(1, 1);
+ // if written == 1
+ {
+ _stream.Write(Threshold, 32);
+ _stream.Write(Rate, 32);
+ }
+ }
+
+ _stream.WriteString(FirstName, 8); // First name
+ _stream.WriteString(LastName, 8); // Last name - not set for WoW
+
+ _stream.Write(AccountId, 32);
+ _stream.Write(Region, 8);
+ _stream.Write(0, 64);
+
+ _stream.Write(GameAccountRegion, 8);
+ _stream.WriteString(GameAccountName, 5, -1);
+ _stream.Write(GameAccountFlags, 64);
+
+ _stream.Write(0, 32);
+ }
+ else
+ {
+ _stream.Write(!Modules.empty(), 1);
+ if (!Modules.empty())
+ {
+ ModuleInfo* info = Modules[0];
+ _stream.WriteBytes(info->Type.c_str(), 4);
+ _stream.WriteFourCC(info->Region);
+ _stream.WriteBytes(info->ModuleId, 32);
+ }
+
+ _stream.Write(ErrorType, 2);
+ if (ErrorType == 1)
+ {
+ _stream.Write(Result, 16);
+ _stream.Write(0x80000000, 32);
+ }
+ }
+}
+
+std::string Battlenet::AuthComplete::ToString() const
+{
+ std::ostringstream stream;
+ stream << "Battlenet::AuthComplete AuthResult " << Result << " PingTimeout " << PingTimeout << " Threshold " << Threshold << " Rate " << Rate
+ << " FirstName " << FirstName << " LastName " << LastName << " AccountId " << AccountId << " Region " << uint32(Region) << " GameAccountName " << GameAccountName
+ << " GameAccountFlags " << GameAccountFlags << " Modules " << Modules.size();
+
+ for (ModuleInfo const* module : Modules)
+ stream << std::endl << "Battlenet::ModuleInfo Locale " << module->Region.c_str() << ", ModuleId " << ByteArrayToHexStr(module->ModuleId, 32) << ", DataSize " << module->DataSize << ", Data " << ByteArrayToHexStr(module->Data, module->DataSize);
+
+ return stream.str();
+}
+
+void Battlenet::AuthComplete::SetAuthResult(AuthResult result)
+{
+ ErrorType = result != AUTH_OK ? 1 : 0;
+ Result = result;
+}
+
+Battlenet::AuthResume::~AuthResume()
+{
+ for (ModuleInfo* m : Modules)
+ delete m;
+}
+
+void Battlenet::AuthResume::Write()
+{
+ _stream.Write(Result != 0, 1);
+ if (Result == 0)
+ {
+ _stream.Write(Modules.size(), 3);
+ for (size_t i = 0; i < Modules.size(); ++i)
+ {
+ ModuleInfo* info = Modules[i];
+ _stream.WriteBytes(info->Type.c_str(), 4);
+ _stream.WriteFourCC(info->Region);
+ _stream.WriteBytes(info->ModuleId, 32);
+ _stream.Write(info->DataSize, 10);
+ _stream.WriteBytes(info->Data, info->DataSize);
+ }
+
+ _stream.Write(PingTimeout + std::numeric_limits<int32>::min(), 32);
+ _stream.Write(1, 1);
+ // if written == 1
+ {
+ _stream.Write(1, 1);
+ // if written == 1
+ {
+ _stream.Write(Threshold, 32);
+ _stream.Write(Rate, 32);
+ }
+ }
+ }
+ else
+ {
+ _stream.Write(!Modules.empty(), 1);
+ if (!Modules.empty())
+ {
+ ModuleInfo* info = Modules[0];
+ _stream.WriteBytes(info->Type.c_str(), 4);
+ _stream.WriteFourCC(info->Region);
+ _stream.WriteBytes(info->ModuleId, 32);
+ }
+
+ _stream.Write(ErrorType, 2);
+ if (ErrorType == 1)
+ {
+ _stream.Write(Result, 16);
+ _stream.Write(0x80000000, 32);
+ }
+ }
+}
+
+std::string Battlenet::AuthResume::ToString() const
+{
+ std::ostringstream stream;
+ stream << "Battlenet::AuthResume AuthResult " << Result << " PingTimeout " << PingTimeout << " Threshold " << Threshold << " Rate " << Rate << " Modules " << Modules.size();
+ for (ModuleInfo const* module : Modules)
+ stream << std::endl << "Battlenet::ModuleInfo Locale " << module->Region.c_str() << ", ModuleId " << ByteArrayToHexStr(module->ModuleId, 32) << ", DataSize " << module->DataSize << ", Data " << ByteArrayToHexStr(module->Data, module->DataSize);
+
+ return stream.str();
+}
+
+void Battlenet::AuthResume::SetAuthResult(AuthResult result)
+{
+ ErrorType = result != AUTH_OK ? 1 : 0;
+ Result = result;
+}
+
+Battlenet::RealmCharacterCounts::~RealmCharacterCounts()
+{
+ for (ServerPacket* realmData : RealmData)
+ delete realmData;
+}
+
+void Battlenet::RealmCharacterCounts::Write()
+{
+ _stream.Write(false, 1); // failure
+ _stream.Write(CharacterCounts.size(), 7);
+ for (CharacterCountEntry const& entry : CharacterCounts)
+ {
+ _stream.Write(entry.Realm.Battlegroup, 8);
+ _stream.Write(entry.Realm.Index, 32);
+ _stream.Write(entry.Realm.Region, 8);
+ _stream.Write(entry.CharacterCount, 16);
+ }
+
+ for (ServerPacket* realmData : RealmData)
+ {
+ realmData->Write();
+ _stream.WriteBytes(realmData->GetData(), realmData->GetSize());
+ }
+}
+
+std::string Battlenet::RealmCharacterCounts::ToString() const
+{
+ std::ostringstream stream;
+ stream << "Battlenet::RealmCharacterCounts Realms " << CharacterCounts.size();
+
+ for (CharacterCountEntry const& entry : CharacterCounts)
+ stream << std::endl << "Region " << uint32(entry.Realm.Region) << " Battlegroup " << uint32(entry.Realm.Region) << " Index " << entry.Realm.Index << " Characters " << entry.CharacterCount;
+
+ for (ServerPacket* realmData : RealmData)
+ stream << std::endl << realmData->ToString();
+
+ return stream.str().c_str();
+}
+
+void Battlenet::RealmUpdate::Write()
+{
+ _stream.Write(true, 1); // Success
+ _stream.Write(Type + -std::numeric_limits<int32>::min(), 32);
+ _stream.WriteFloat(Population);
+ _stream.Write(Flags, 8);
+ _stream.Write(Lock, 8);
+ _stream.Write(Timezone, 32);
+ _stream.Write(!Version.empty(), 1);
+ if (!Version.empty())
+ {
+ _stream.WriteString(Version, 5);
+ _stream.Write(Build, 32);
+
+ uint32 ip = Address.get_ip_address();
+ uint16 port = Address.get_port_number();
+
+ EndianConvertReverse(ip);
+ EndianConvertReverse(port);
+
+ _stream.WriteBytes(&ip, 4);
+ _stream.WriteBytes(&port, 2);
+ }
+
+ _stream.WriteString(Name, 10);
+
+ _stream.Write(Battlegroup, 8);
+ _stream.Write(Index, 32);
+ _stream.Write(Region, 8);
+}
+
+std::string Battlenet::RealmUpdate::ToString() const
+{
+ std::ostringstream stream;
+ stream << "Battlenet::RealmUpdate Timezone " << Timezone << " Population " << Population << " Lock " << uint32(Lock) << " Type " << Type << " Name " << Name
+ << " Flags " << uint32(Flags) << " Region " << uint32(Region) << " Battlegroup " << uint32(Battlegroup) << " Index " << Index;
+
+ if (!Version.empty())
+ stream << " Version " << Version;
+
+ return stream.str().c_str();
+}
+
+void Battlenet::RealmJoinRequest::Read()
+{
+ Realm.Battlegroup = _stream.Read<uint8>(8);
+ Realm.Index = _stream.Read<uint32>(32);
+ Realm.Region = _stream.Read<uint8>(8);
+ ClientSeed = _stream.Read<uint32>(32);
+}
+
+std::string Battlenet::RealmJoinRequest::ToString() const
+{
+ std::ostringstream stream;
+ stream << "Battlenet::RealmJoinRequest ClientSeed " << ClientSeed << " Region " << uint32(Realm.Region) << " Battlegroup " << uint32(Realm.Battlegroup) << " Index " << Realm.Index;
+ return stream.str().c_str();
+}
+
+void Battlenet::RealmJoinResult::Write()
+{
+ _stream.Write(0, 27);
+ _stream.Write(0, 1); // Fail
+ _stream.Write(ServerSeed, 32);
+ _stream.Write(0, 5); // IPv6 addresses
+ _stream.Write(IPv4.size(), 5);
+ for (ACE_INET_Addr const& addr : IPv4)
+ {
+ uint32 ip = addr.get_ip_address();
+ uint16 port = addr.get_port_number();
+
+ EndianConvertReverse(ip);
+ EndianConvertReverse(port);
+
+ _stream.WriteBytes(&ip, 4);
+ _stream.WriteBytes(&port, 2);
+ }
+}
+
+std::string Battlenet::RealmJoinResult::ToString() const
+{
+ std::ostringstream stream;
+ stream << "Battlenet::RealmJoinResult ServerSeed " << ServerSeed << " IPv4 Addresses " << IPv4.size();
+ for (ACE_INET_Addr const& addr : IPv4)
+ stream << std::endl << "Battlenet::RealmJoinResult::Address " << GetAddressString(addr);
+
+ return stream.str().c_str();
+}
diff --git a/src/server/authserver/Server/BattlenetPackets.h b/src/server/authserver/Server/BattlenetPackets.h
new file mode 100644
index 00000000000..54fb50184a0
--- /dev/null
+++ b/src/server/authserver/Server/BattlenetPackets.h
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __BATTLENETPACKETS_H__
+#define __BATTLENETPACKETS_H__
+
+#include "AuthCodes.h"
+#include "BattlenetBitStream.h"
+#include "BattlenetManager.h"
+#include "Define.h"
+#include "Errors.h"
+#include <string>
+
+namespace Battlenet
+{
+ class BitStream;
+
+ enum Channel
+ {
+ AUTHENTICATION = 0,
+ CREEP = 1,
+ WOW = 2
+ };
+
+ enum AuthOpcode
+ {
+ CMSG_AUTH_CHALLENGE = 0x0,
+ CMSG_AUTH_RECONNECT = 0x1,
+ CMSG_AUTH_PROOF_RESPONSE = 0x2,
+
+ SMSG_AUTH_COMPLETE = 0x0,
+ SMSG_AUTH_RESUME = 0x1,
+ SMSG_AUTH_PROOF_REQUEST = 0x2
+ };
+
+ enum CreepOpcodes
+ {
+ CMSG_PING = 0x0,
+ CMSG_ENABLE_ENCRYPTION = 0x5,
+ CMSG_DISCONNECT = 0x6,
+ CMSG_INVALID_PACKET = 0x9,
+
+ SMSG_PONG = 0x0
+ };
+
+ enum WoWOpcodes
+ {
+ CMSG_REALM_UPDATE_SUBSCRIBE = 0x0,
+ CMSG_REALM_UPDATE_UNSUBSCRIBE = 0x1,
+ CMSG_JOIN_REQUEST = 0x8,
+
+ SMSG_CHARACTER_COUNTS = 0x0,
+ SMSG_REALM_UPDATE = 0x2,
+ SMSG_REALM_UPDATE_END = 0x3,
+ SMSG_JOIN_RESULT = 0x8
+ };
+
+ struct PacketHeader
+ {
+ PacketHeader(uint32 opcode, uint32 channel) : Opcode(opcode), Channel(channel) { }
+ PacketHeader() : Opcode(0), Channel(AUTHENTICATION) { }
+
+ uint32 Opcode;
+ int32 Channel;
+
+ bool operator<(PacketHeader const& right) const
+ {
+ if (Opcode < right.Opcode)
+ return true;
+ if (Opcode > right.Opcode)
+ return false;
+
+ return Channel < right.Channel;
+ }
+
+ bool operator==(PacketHeader const& right) const
+ {
+ return Opcode == right.Opcode && Channel == right.Channel;
+ }
+
+ std::string ToString() const;
+ };
+
+ class Packet
+ {
+ public:
+ Packet(PacketHeader const& header, BitStream& stream) : _header(header), _stream(stream) { }
+ virtual ~Packet() { }
+
+ PacketHeader const& GetHeader() const { return _header; }
+
+ virtual void Write() = 0;
+ virtual void Read() = 0;
+
+ virtual std::string ToString() const = 0;
+
+ protected:
+ PacketHeader _header;
+ BitStream& _stream;
+
+ private:
+ Packet(Packet const& right);
+ Packet& operator=(Packet const& right);
+ };
+
+ class ClientPacket : public Packet
+ {
+ public:
+ ClientPacket(PacketHeader const& header, BitStream& stream) : Packet(header, stream) { }
+
+ void Write() override final { ASSERT(!"Write not implemented for this packet."); }
+ };
+
+ class ServerPacket : public Packet
+ {
+ public:
+ ServerPacket(PacketHeader const& header);
+ ~ServerPacket();
+
+ void Read() override final { ASSERT(!"Read not implemented for server packets."); }
+
+ uint8 const* GetData() const { return _stream.GetBuffer(); }
+ size_t GetSize() const { return _stream.GetSize(); }
+ };
+
+ class AuthChallenge final : public ClientPacket
+ {
+ public:
+ AuthChallenge(PacketHeader const& header, BitStream& stream) : ClientPacket(header, stream)
+ {
+ ASSERT(header == PacketHeader(CMSG_AUTH_CHALLENGE, AUTHENTICATION) && "Invalid packet header for AuthChallenge");
+ }
+
+ void Read() override;
+ std::string ToString() const override;
+
+ std::string Program;
+ std::string Platform;
+ std::string Locale;
+ std::vector<Component> Components;
+ std::string Login;
+ };
+
+ class AuthResumeInfo final : public ClientPacket
+ {
+ public:
+ AuthResumeInfo(PacketHeader const& header, BitStream& stream) : ClientPacket(header, stream)
+ {
+ ASSERT(header == PacketHeader(CMSG_AUTH_RECONNECT, AUTHENTICATION) && "Invalid packet header for AuthResumeInfo");
+ }
+
+ void Read() override;
+ std::string ToString() const override;
+
+ std::string Program;
+ std::string Platform;
+ std::string Locale;
+ std::vector<Component> Components;
+ std::string Login;
+ uint8 Region;
+ std::string GameAccountName;
+ };
+
+ class ProofRequest final : public ServerPacket
+ {
+ public:
+ ProofRequest() : ServerPacket(PacketHeader(SMSG_AUTH_PROOF_REQUEST, AUTHENTICATION)) { }
+ ~ProofRequest();
+
+ void Write() override;
+ std::string ToString() const override;
+
+ std::vector<ModuleInfo*> Modules;
+ };
+
+ class ProofResponse final : public ClientPacket
+ {
+ public:
+ ProofResponse(PacketHeader const& header, BitStream& stream) : ClientPacket(header, stream)
+ {
+ ASSERT(header == PacketHeader(CMSG_AUTH_PROOF_RESPONSE, AUTHENTICATION) && "Invalid packet header for ProofResponse");
+ }
+
+ ~ProofResponse();
+
+ void Read() override;
+ std::string ToString() const override;
+
+ std::vector<BitStream*> Modules;
+ };
+
+ class AuthComplete final : public ServerPacket
+ {
+ public:
+ AuthComplete() : ServerPacket(PacketHeader(SMSG_AUTH_COMPLETE, AUTHENTICATION)),
+ Result(AUTH_OK), ErrorType(0), PingTimeout(120000), Threshold(25000000), Rate(1000),
+ FirstName(""), LastName(""), AccountId(0), Region(2), GameAccountRegion(2), GameAccountName("")
+ {
+ }
+
+ ~AuthComplete();
+
+ void Write() override;
+ std::string ToString() const override;
+
+ std::vector<ModuleInfo*> Modules;
+ void SetAuthResult(AuthResult result);
+ AuthResult Result;
+ uint32 ErrorType;
+
+ int32 PingTimeout;
+ uint32 Threshold;
+ uint32 Rate;
+ std::string FirstName;
+ std::string LastName;
+ uint32 AccountId;
+ uint8 Region;
+ uint8 GameAccountRegion;
+ std::string GameAccountName;
+ uint64 GameAccountFlags;
+ };
+
+ class AuthResume final : public ServerPacket
+ {
+ public:
+ AuthResume() : ServerPacket(PacketHeader(SMSG_AUTH_RESUME, AUTHENTICATION)),
+ Result(AUTH_OK), ErrorType(0), PingTimeout(120000), Threshold(25000000), Rate(1000)
+ {
+ }
+
+ ~AuthResume();
+
+ void Write() override;
+ std::string ToString() const override;
+
+ std::vector<ModuleInfo*> Modules;
+ void SetAuthResult(AuthResult result);
+ AuthResult Result;
+ uint32 ErrorType;
+
+ int32 PingTimeout;
+ uint32 Threshold;
+ uint32 Rate;
+ };
+
+ class Pong final : public ServerPacket
+ {
+ public:
+ Pong() : ServerPacket(PacketHeader(SMSG_PONG, CREEP))
+ {
+ }
+
+ void Write() override { }
+ std::string ToString() const override { return "Battlenet::Pong"; }
+ };
+
+ class RealmCharacterCounts final : public ServerPacket
+ {
+ public:
+ RealmCharacterCounts() : ServerPacket(PacketHeader(SMSG_CHARACTER_COUNTS, WOW))
+ {
+ }
+ ~RealmCharacterCounts();
+
+ struct CharacterCountEntry
+ {
+ RealmId Realm;
+ uint32 CharacterCount;
+ };
+
+ void Write() override;
+ std::string ToString() const override;
+
+ std::vector<CharacterCountEntry> CharacterCounts;
+ std::vector<ServerPacket*> RealmData;
+ };
+
+ class RealmUpdate final : public ServerPacket
+ {
+ public:
+ RealmUpdate() : ServerPacket(PacketHeader(SMSG_REALM_UPDATE, WOW)),
+ Timezone(0), Population(0.0f), Lock(0), Type(0), Name(""), Version(""),
+ Address(), Flags(0), Region(0), Battlegroup(0), Index(0), Build(0)
+ {
+ }
+
+ void Write() override;
+ std::string ToString() const override;
+
+ uint32 Timezone;
+ float Population;
+ uint8 Lock;
+ uint32 Type;
+ std::string Name;
+ std::string Version;
+ ACE_INET_Addr Address;
+ uint8 Flags;
+ uint8 Region;
+ uint8 Battlegroup;
+ uint32 Index;
+ uint32 Build;
+ };
+
+ class RealmUpdateComplete final : public ServerPacket
+ {
+ public:
+ RealmUpdateComplete() : ServerPacket(PacketHeader(SMSG_REALM_UPDATE_END, WOW))
+ {
+ }
+
+ void Write() override { }
+ std::string ToString() const override { return "Battlenet::RealmUpdateComplete"; }
+ };
+
+ class RealmJoinRequest final : public ClientPacket
+ {
+ public:
+ RealmJoinRequest(PacketHeader const& header, BitStream& stream) : ClientPacket(header, stream)
+ {
+ ASSERT(header == PacketHeader(CMSG_JOIN_REQUEST, WOW) && "Invalid packet header for RealmJoinRequest");
+ }
+
+ void Read() override;
+ std::string ToString() const override;
+
+ uint32 ClientSeed;
+ uint32 Unknown;
+ RealmId Realm;
+ };
+
+ class RealmJoinResult final : public ServerPacket
+ {
+ public:
+ RealmJoinResult() : ServerPacket(PacketHeader(SMSG_JOIN_RESULT, WOW)), ServerSeed(0)
+ {
+ }
+
+ void Write() override;
+ std::string ToString() const override;
+
+ uint32 ServerSeed;
+ std::vector<ACE_INET_Addr> IPv4;
+ };
+}
+
+#endif // __BATTLENETPACKETS_H__
diff --git a/src/server/authserver/Server/BattlenetSocket.cpp b/src/server/authserver/Server/BattlenetSocket.cpp
new file mode 100644
index 00000000000..4ee3c8e9b3d
--- /dev/null
+++ b/src/server/authserver/Server/BattlenetSocket.cpp
@@ -0,0 +1,1011 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "AuthCodes.h"
+#include "BattlenetBitStream.h"
+#include "BattlenetSocket.h"
+#include "Database/DatabaseEnv.h"
+#include "HmacHash.h"
+#include "Log.h"
+#include "RealmList.h"
+#include "SHA256.h"
+#include <map>
+
+uint32 const Battlenet::Socket::SRP6_V_Size = 128;
+uint32 const Battlenet::Socket::SRP6_S_Size = 32;
+
+std::map<Battlenet::PacketHeader, Battlenet::Socket::PacketHandler> InitHandlers()
+{
+ std::map<Battlenet::PacketHeader, Battlenet::Socket::PacketHandler> handlers;
+
+ handlers[Battlenet::PacketHeader(Battlenet::CMSG_AUTH_CHALLENGE, Battlenet::AUTHENTICATION)] = &Battlenet::Socket::HandleAuthChallenge;
+ handlers[Battlenet::PacketHeader(Battlenet::CMSG_AUTH_RECONNECT, Battlenet::AUTHENTICATION)] = &Battlenet::Socket::HandleAuthReconnect;
+ handlers[Battlenet::PacketHeader(Battlenet::CMSG_AUTH_PROOF_RESPONSE, Battlenet::AUTHENTICATION)] = &Battlenet::Socket::HandleAuthProofResponse;
+
+ handlers[Battlenet::PacketHeader(Battlenet::CMSG_PING, Battlenet::CREEP)] = &Battlenet::Socket::HandlePing;
+ handlers[Battlenet::PacketHeader(Battlenet::CMSG_ENABLE_ENCRYPTION, Battlenet::CREEP)] = &Battlenet::Socket::HandleEnableEncryption;
+ handlers[Battlenet::PacketHeader(Battlenet::CMSG_DISCONNECT, Battlenet::CREEP)] = &Battlenet::Socket::HandleDisconnect;
+
+ handlers[Battlenet::PacketHeader(Battlenet::CMSG_REALM_UPDATE_SUBSCRIBE, Battlenet::WOW)] = &Battlenet::Socket::HandleRealmUpdateSubscribe;
+ handlers[Battlenet::PacketHeader(Battlenet::CMSG_JOIN_REQUEST, Battlenet::WOW)] = &Battlenet::Socket::HandleRealmJoinRequest;
+
+ return handlers;
+}
+
+std::map<Battlenet::PacketHeader, Battlenet::Socket::PacketHandler> Handlers = InitHandlers();
+
+Battlenet::Socket::ModuleHandler const Battlenet::Socket::ModuleHandlers[MODULE_COUNT] =
+{
+ &Battlenet::Socket::HandlePasswordModule,
+ &Battlenet::Socket::UnhandledModule,
+ &Battlenet::Socket::UnhandledModule,
+ &Battlenet::Socket::HandleSelectGameAccountModule,
+ &Battlenet::Socket::HandleRiskFingerprintModule,
+ &Battlenet::Socket::HandleResumeModule,
+};
+
+Battlenet::Socket::Socket(RealmSocket& socket) : _socket(socket), _accountId(0), _accountName(), _locale(),
+ _os(), _build(0), _gameAccountId(0), _gameAccountIndex(0), _accountSecurityLevel(SEC_PLAYER), I(), s(), v(), b(), B(), K(),
+ _reconnectProof(), _crypt(), _authed(false)
+{
+ static uint8 const N_Bytes[] =
+ {
+ 0xAB, 0x24, 0x43, 0x63, 0xA9, 0xC2, 0xA6, 0xC3, 0x3B, 0x37, 0xE4, 0x61, 0x84, 0x25, 0x9F, 0x8B,
+ 0x3F, 0xCB, 0x8A, 0x85, 0x27, 0xFC, 0x3D, 0x87, 0xBE, 0xA0, 0x54, 0xD2, 0x38, 0x5D, 0x12, 0xB7,
+ 0x61, 0x44, 0x2E, 0x83, 0xFA, 0xC2, 0x21, 0xD9, 0x10, 0x9F, 0xC1, 0x9F, 0xEA, 0x50, 0xE3, 0x09,
+ 0xA6, 0xE5, 0x5E, 0x23, 0xA7, 0x77, 0xEB, 0x00, 0xC7, 0xBA, 0xBF, 0xF8, 0x55, 0x8A, 0x0E, 0x80,
+ 0x2B, 0x14, 0x1A, 0xA2, 0xD4, 0x43, 0xA9, 0xD4, 0xAF, 0xAD, 0xB5, 0xE1, 0xF5, 0xAC, 0xA6, 0x13,
+ 0x1C, 0x69, 0x78, 0x64, 0x0B, 0x7B, 0xAF, 0x9C, 0xC5, 0x50, 0x31, 0x8A, 0x23, 0x08, 0x01, 0xA1,
+ 0xF5, 0xFE, 0x31, 0x32, 0x7F, 0xE2, 0x05, 0x82, 0xD6, 0x0B, 0xED, 0x4D, 0x55, 0x32, 0x41, 0x94,
+ 0x29, 0x6F, 0x55, 0x7D, 0xE3, 0x0F, 0x77, 0x19, 0xE5, 0x6C, 0x30, 0xEB, 0xDE, 0xF6, 0xA7, 0x86
+ };
+
+ N.SetBinary(N_Bytes, sizeof(N_Bytes));
+ g.SetDword(2);
+
+ SHA256Hash sha;
+ sha.UpdateBigNumbers(&N, &g, NULL);
+ sha.Finalize();
+ k.SetBinary(sha.GetDigest(), sha.GetLength());
+}
+
+void Battlenet::Socket::_SetVSFields(std::string const& pstr)
+{
+ s.SetRand(SRP6_S_Size * 8);
+
+ BigNumber p;
+ p.SetHexStr(pstr.c_str());
+
+ SHA256Hash sha;
+ sha.UpdateBigNumbers(&s, &p, NULL);
+ sha.Finalize();
+ BigNumber x;
+ x.SetBinary(sha.GetDigest(), sha.GetLength());
+ v = g.ModExp(x, N);
+
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_VS_FIELDS);
+ stmt->setString(0, v.AsHexStr());
+ stmt->setString(1, s.AsHexStr());
+ stmt->setString(2, _accountName);
+
+ LoginDatabase.Execute(stmt);
+}
+
+bool Battlenet::Socket::HandleAuthChallenge(PacketHeader& header, BitStream& packet)
+{
+ // Verify that this IP is not in the ip_banned table
+ LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS));
+
+ std::string const& ip_address = _socket.getRemoteAddress();
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_BANNED);
+ stmt->setString(0, ip_address);
+ if (PreparedQueryResult result = LoginDatabase.Query(stmt))
+ {
+ AuthComplete complete;
+ complete.SetAuthResult(LOGIN_BANNED);
+ Send(complete);
+ TC_LOG_DEBUG("server.battlenet", "[Battlenet::AuthChallenge] Banned ip '%s:%d' tries to login!", _socket.getRemoteAddress().c_str(), _socket.getRemotePort());
+ return true;
+ }
+
+ AuthChallenge info(header, packet);
+ info.Read();
+
+ if (info.Program != "WoW")
+ {
+ AuthComplete complete;
+ complete.SetAuthResult(AUTH_INVALID_PROGRAM);
+ Send(complete);
+ return true;
+ }
+
+ if (!sBattlenetMgr->HasPlatform(info.Platform))
+ {
+ AuthComplete complete;
+ complete.SetAuthResult(AUTH_INVALID_OS);
+ Send(complete);
+ return true;
+ }
+
+ if (!sBattlenetMgr->HasPlatform(info.Locale))
+ {
+ AuthComplete complete;
+ complete.SetAuthResult(AUTH_UNSUPPORTED_LANGUAGE);
+ Send(complete);
+ return true;
+ }
+
+ for (Component const& component : info.Components)
+ {
+ if (!sBattlenetMgr->HasComponent(&component))
+ {
+ AuthComplete complete;
+ if (!sBattlenetMgr->HasProgram(component.Program))
+ complete.SetAuthResult(AUTH_INVALID_PROGRAM);
+ else if (!sBattlenetMgr->HasPlatform(component.Platform))
+ complete.SetAuthResult(AUTH_INVALID_OS);
+ else
+ {
+ if (component.Program != "WoW" || AuthHelper::IsBuildSupportingBattlenet(component.Build))
+ complete.SetAuthResult(AUTH_REGION_BAD_VERSION);
+ else
+ complete.SetAuthResult(AUTH_USE_GRUNT_LOGON);
+ }
+
+ Send(complete);
+ return true;
+ }
+
+ if (component.Platform == "base")
+ _build = component.Build;
+ }
+
+ _accountName = info.Login;
+ _locale = info.Locale;
+ _os = info.Platform;
+
+ Utf8ToUpperOnlyLatin(_accountName);
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ACCOUNT_INFO);
+ stmt->setString(0, _accountName);
+
+ PreparedQueryResult result = LoginDatabase.Query(stmt);
+ if (!result)
+ {
+ AuthComplete complete;
+ complete.SetAuthResult(AUTH_UNKNOWN_ACCOUNT);
+ Send(complete);
+ return true;
+ }
+
+ Field* fields = result->Fetch();
+
+ _accountId = fields[1].GetUInt32();
+
+ // If the IP is 'locked', check that the player comes indeed from the correct IP address
+ if (fields[2].GetUInt8() == 1) // if ip is locked
+ {
+ TC_LOG_DEBUG("server.battlenet", "[Battlenet::AuthChallenge] Account '%s' is locked to IP - '%s' is logging in from '%s'", _accountName.c_str(), fields[4].GetCString(), ip_address.c_str());
+
+ if (strcmp(fields[4].GetCString(), ip_address.c_str()) != 0)
+ {
+ AuthComplete complete;
+ complete.SetAuthResult(AUTH_ACCOUNT_LOCKED);
+ Send(complete);
+ return true;
+ }
+ }
+ else
+ {
+ TC_LOG_DEBUG("server.battlenet", "[Battlenet::AuthChallenge] Account '%s' is not locked to ip", _accountName.c_str());
+ std::string accountCountry = fields[3].GetString();
+ if (accountCountry.empty() || accountCountry == "00")
+ TC_LOG_DEBUG("server.battlenet", "[Battlenet::AuthChallenge] Account '%s' is not locked to country", _accountName.c_str());
+ else if (!accountCountry.empty())
+ {
+ uint32 ip = inet_addr(ip_address.c_str());
+ EndianConvertReverse(ip);
+
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGON_COUNTRY);
+ stmt->setUInt32(0, ip);
+ if (PreparedQueryResult sessionCountryQuery = LoginDatabase.Query(stmt))
+ {
+ std::string loginCountry = (*sessionCountryQuery)[0].GetString();
+ TC_LOG_DEBUG("server.battlenet", "[Battlenet::AuthChallenge] Account '%s' is locked to country: '%s' Player country is '%s'", _accountName.c_str(), accountCountry.c_str(), loginCountry.c_str());
+ if (loginCountry != accountCountry)
+ {
+ AuthComplete complete;
+ complete.SetAuthResult(AUTH_ACCOUNT_LOCKED);
+ Send(complete);
+ return true;
+ }
+ }
+ }
+ }
+
+ //set expired bans to inactive
+ LoginDatabase.DirectExecute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_BNET_EXPIRED_BANS));
+
+ // If the account is banned, reject the logon attempt
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ACTIVE_ACCOUNT_BAN);
+ stmt->setUInt32(0, _accountId);
+ PreparedQueryResult banresult = LoginDatabase.Query(stmt);
+ if (banresult)
+ {
+ Field* fields = banresult->Fetch();
+ if (fields[0].GetUInt32() == fields[1].GetUInt32())
+ {
+ AuthComplete complete;
+ complete.SetAuthResult(LOGIN_BANNED);
+ Send(complete);
+ TC_LOG_DEBUG("server.battlenet", "'%s:%d' [Battlenet::AuthChallenge] Banned account %s tried to login!", _socket.getRemoteAddress().c_str(), _socket.getRemotePort(), _accountName.c_str());
+ return true;
+ }
+ else
+ {
+ AuthComplete complete;
+ complete.SetAuthResult(LOGIN_SUSPENDED);
+ Send(complete);
+ TC_LOG_DEBUG("server.battlenet", "'%s:%d' [Battlenet::AuthChallenge] Temporarily banned account %s tried to login!", _socket.getRemoteAddress().c_str(), _socket.getRemotePort(), _accountName.c_str());
+ return true;
+ }
+ }
+
+ SHA256Hash sha;
+ sha.UpdateData(_accountName);
+ sha.Finalize();
+
+ I.SetBinary(sha.GetDigest(), sha.GetLength());
+
+ ModuleInfo* password = sBattlenetMgr->CreateModule(_os, "Password");
+ ModuleInfo* thumbprint = sBattlenetMgr->CreateModule(_os, "Thumbprint");
+
+ std::string pStr = fields[0].GetString();
+
+ std::string databaseV = fields[5].GetString();
+ std::string databaseS = fields[6].GetString();
+
+ if (databaseV.size() != SRP6_V_Size * 2 || databaseS.size() != SRP6_S_Size * 2)
+ _SetVSFields(pStr);
+ else
+ {
+ s.SetHexStr(databaseS.c_str());
+ v.SetHexStr(databaseV.c_str());
+ }
+
+ b.SetRand(128 * 8);
+ B = ((v * k) + g.ModExp(b, N)) % N;
+ BigNumber unk;
+ unk.SetRand(128 * 8);
+
+ BitStream passwordData;
+ uint8 state = 0;
+ passwordData.WriteBytes(&state, 1);
+ passwordData.WriteBytes(I.AsByteArray(32).get(), 32);
+ passwordData.WriteBytes(s.AsByteArray(32).get(), 32);
+ passwordData.WriteBytes(B.AsByteArray(128).get(), 128);
+ passwordData.WriteBytes(unk.AsByteArray(128).get(), 128);
+
+ password->DataSize = passwordData.GetSize();
+ password->Data = new uint8[password->DataSize];
+ memcpy(password->Data, passwordData.GetBuffer(), password->DataSize);
+
+ _modulesWaitingForData.push(MODULE_PASSWORD);
+
+ ProofRequest request;
+ request.Modules.push_back(password);
+ // if has authenticator, send Token module
+ request.Modules.push_back(thumbprint);
+ Send(request);
+ return true;
+}
+
+bool Battlenet::Socket::HandleAuthReconnect(PacketHeader& header, BitStream& packet)
+{
+ AuthResumeInfo reconnect(header, packet);
+ reconnect.Read();
+
+ TC_LOG_DEBUG("server.battlenet", "%s", reconnect.ToString().c_str());
+
+ _accountName = reconnect.Login;
+ _locale = reconnect.Locale;
+ _os = reconnect.Platform;
+ auto baseComponent = std::find_if(reconnect.Components.begin(), reconnect.Components.end(), [](Component const& c) { return c.Program == "base"; });
+ if (baseComponent != reconnect.Components.end())
+ _build = baseComponent->Build;
+
+ uint8 accountIndex = atol(reconnect.GameAccountName.substr(reconnect.GameAccountName.find_last_of('#') + 1).c_str());
+
+ Utf8ToUpperOnlyLatin(_accountName);
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_RECONNECT_INFO);
+ stmt->setString(0, _accountName);
+ stmt->setUInt8(1, accountIndex);
+ PreparedQueryResult result = LoginDatabase.Query(stmt);
+ if (!result)
+ {
+ AuthResume resume;
+ resume.SetAuthResult(AUTH_UNKNOWN_ACCOUNT);
+ Send(resume);
+ return false;
+ }
+
+ Field* fields = result->Fetch();
+
+ _accountId = fields[0].GetUInt32();
+ K.SetHexStr(fields[1].GetString().c_str());
+ _gameAccountId = fields[2].GetUInt32();
+ _gameAccountIndex = accountIndex;
+
+ ModuleInfo* thumbprint = sBattlenetMgr->CreateModule(_os, "Thumbprint");
+ ModuleInfo* resume = sBattlenetMgr->CreateModule(_os, "Resume");
+ BitStream resumeData;
+ uint8 state = 0;
+ _reconnectProof.SetRand(16 * 8);
+
+ resumeData.WriteBytes(&state, 1);
+ resumeData.WriteBytes(_reconnectProof.AsByteArray().get(), 16);
+
+ resume->DataSize = resumeData.GetSize();
+ resume->Data = new uint8[resume->DataSize];
+ memcpy(resume->Data, resumeData.GetBuffer(), resume->DataSize);
+
+ _modulesWaitingForData.push(MODULE_RESUME);
+
+ ProofRequest request;
+ request.Modules.push_back(thumbprint);
+ request.Modules.push_back(resume);
+ Send(request);
+ return true;
+}
+
+bool Battlenet::Socket::HandleAuthProofResponse(PacketHeader& header, BitStream& packet)
+{
+ ProofResponse proof(header, packet);
+ proof.Read();
+
+ if (_modulesWaitingForData.size() < proof.Modules.size())
+ {
+ AuthComplete complete;
+ complete.SetAuthResult(AUTH_CORRUPTED_MODULE);
+ Send(complete);
+ return true;
+ }
+
+ ServerPacket* response = nullptr;
+ for (size_t i = 0; i < proof.Modules.size(); ++i)
+ {
+ if (!(this->*(ModuleHandlers[_modulesWaitingForData.front()]))(proof.Modules[i], &response))
+ break;
+
+ _modulesWaitingForData.pop();
+ }
+
+ if (!response)
+ {
+ response = new AuthComplete();
+ static_cast<AuthComplete*>(response)->SetAuthResult(AUTH_INTERNAL_ERROR);
+ }
+
+ Send(*response);
+ delete response;
+ return true;
+}
+
+bool Battlenet::Socket::HandlePing(PacketHeader& /*header*/, BitStream& /*packet*/)
+{
+ Pong pong;
+ Send(pong);
+ return true;
+}
+
+bool Battlenet::Socket::HandleEnableEncryption(PacketHeader& /*header*/, BitStream& /*packet*/)
+{
+ _crypt.Init(&K);
+ return true;
+}
+
+bool Battlenet::Socket::HandleDisconnect(PacketHeader& /*header*/, BitStream& /*packet*/)
+{
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_SESSION_KEY);
+ stmt->setString(0, "");
+ stmt->setUInt32(1, _accountId);
+ LoginDatabase.Execute(stmt);
+ return true;
+}
+
+bool Battlenet::Socket::HandleRealmUpdateSubscribe(PacketHeader& /*header*/, BitStream& /*packet*/)
+{
+ sRealmList->UpdateIfNeed();
+
+ RealmCharacterCounts counts;
+
+ ACE_INET_Addr clientAddr;
+ _socket.peer().get_remote_addr(clientAddr);
+
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS);
+ stmt->setUInt32(0, _gameAccountId);
+
+ if (PreparedQueryResult countResult = LoginDatabase.Query(stmt))
+ {
+ do
+ {
+ Field* fields = countResult->Fetch();
+ uint32 build = fields[4].GetUInt32();
+ counts.CharacterCounts.push_back({ { fields[2].GetUInt8(), fields[3].GetUInt8(), fields[1].GetUInt32(), (_build != build ? build : 0) }, fields[0].GetUInt8() });
+ } while (countResult->NextRow());
+ }
+
+ for (RealmList::RealmMap::const_iterator i = sRealmList->begin(); i != sRealmList->end(); ++i)
+ {
+ Realm const& realm = i->second;
+
+ uint32 flag = realm.flag;
+ RealmBuildInfo const* buildInfo = AuthHelper::GetBuildInfo(realm.gamebuild);
+ if (realm.gamebuild != _build)
+ {
+ if (!buildInfo)
+ continue;
+
+ flag |= REALM_FLAG_OFFLINE | REALM_FLAG_SPECIFYBUILD; // tell the client what build the realm is for
+ }
+
+ if (!buildInfo)
+ flag &= ~REALM_FLAG_SPECIFYBUILD;
+
+ RealmUpdate* update = new RealmUpdate();
+ update->Timezone = realm.timezone;
+ update->Population = realm.populationLevel;
+ update->Lock = (realm.allowedSecurityLevel > _accountSecurityLevel) ? 1 : 0;
+ update->Type = realm.icon;
+ update->Name = realm.name;
+
+ if (flag & REALM_FLAG_SPECIFYBUILD)
+ {
+ std::ostringstream version;
+ version << buildInfo->MajorVersion << '.' << buildInfo->MinorVersion << '.' << buildInfo->BugfixVersion << '.' << buildInfo->Build;
+
+ update->Version = version.str();
+ update->Address = realm.GetAddressForClient(clientAddr);
+ update->Build = buildInfo->Build;
+ }
+
+ update->Flags = flag;
+ update->Region = realm.Region;
+ update->Battlegroup = realm.Battlegroup;
+ update->Index = realm.m_ID;
+
+ counts.RealmData.push_back(update);
+ }
+
+ counts.RealmData.push_back(new RealmUpdateComplete());
+
+ Send(counts);
+ return true;
+}
+
+bool Battlenet::Socket::HandleRealmJoinRequest(PacketHeader& header, BitStream& packet)
+{
+ RealmJoinRequest join(header, packet);
+ join.Read();
+
+ RealmJoinResult result;
+ Realm const* realm = sRealmList->GetRealm(join.Realm);
+ if (!realm || realm->flag & (REALM_FLAG_INVALID | REALM_FLAG_OFFLINE))
+ {
+ Send(result);
+ return true;
+ }
+
+ result.ServerSeed = uint32(rand32());
+
+ uint8 sessionKey[40];
+ HmacSha1 hmac(K.GetNumBytes(), K.AsByteArray().get());
+ hmac.UpdateData((uint8*)"WoW\0", 4);
+ hmac.UpdateData((uint8*)&join.ClientSeed, 4);
+ hmac.UpdateData((uint8*)&result.ServerSeed, 4);
+ hmac.Finalize();
+
+ memcpy(sessionKey, hmac.GetDigest(), hmac.GetLength());
+
+ HmacSha1 hmac2(K.GetNumBytes(), K.AsByteArray().get());
+ hmac2.UpdateData((uint8*)"WoW\0", 4);
+ hmac2.UpdateData((uint8*)&result.ServerSeed, 4);
+ hmac2.UpdateData((uint8*)&join.ClientSeed, 4);
+ hmac2.Finalize();
+
+ memcpy(sessionKey + hmac.GetLength(), hmac2.GetDigest(), hmac2.GetLength());
+
+ LoginDatabase.DirectPExecute("UPDATE account SET sessionkey = '%s', last_ip = '%s', last_login = NOW(), locale = %u, failed_logins = 0, os = '%s' WHERE id = %u",
+ ByteArrayToHexStr(sessionKey, 40, true).c_str(), _socket.getRemoteAddress().c_str(), GetLocaleByName(_locale), _os.c_str(), _gameAccountId);
+
+ result.IPv4.push_back(realm->ExternalAddress);
+ if (realm->ExternalAddress != realm->LocalAddress)
+ result.IPv4.push_back(realm->LocalAddress);
+
+ Send(result);
+ return true;
+}
+
+void Battlenet::Socket::OnRead()
+{
+ size_t length = _socket.recv_len();
+ if (!length)
+ return;
+
+ BitStream packet(length);
+ if (!_socket.recv((char*)packet.GetBuffer(), length))
+ return;
+
+ _crypt.DecryptRecv(packet.GetBuffer(), length);
+
+ while (!packet.IsRead())
+ {
+ try
+ {
+ PacketHeader header;
+ header.Opcode = packet.Read<uint32>(6);
+ if (packet.Read<bool>(1))
+ header.Channel = packet.Read<int32>(4);
+
+ if (header.Channel != AUTHENTICATION && !_authed)
+ {
+ TC_LOG_DEBUG("server.battlenet", "Battlenet::Socket::OnRead Received not allowed packet %s", header.ToString().c_str());
+ _socket.shutdown();
+ return;
+ }
+
+ TC_LOG_TRACE("server.battlenet", "Battlenet::Socket::OnRead %s", header.ToString().c_str());
+ std::map<PacketHeader, PacketHandler>::const_iterator itr = Handlers.find(header);
+ if (itr != Handlers.end())
+ {
+ if ((this->*(itr->second))(header, packet))
+ break;
+ }
+ else
+ {
+ TC_LOG_DEBUG("server.battlenet", "Battlenet::Socket::OnRead Unhandled opcode %s", header.ToString().c_str());
+ return;
+ }
+
+ packet.AlignToNextByte();
+ }
+ catch (BitStreamPositionException const& e)
+ {
+ TC_LOG_ERROR("server.battlenet", "Battlenet::Socket::OnRead Exception: %s", e.what());
+ _socket.shutdown();
+ return;
+ }
+ }
+}
+
+void Battlenet::Socket::OnAccept()
+{
+ TC_LOG_TRACE("server.battlenet", "Battlenet::Socket::OnAccept");
+}
+
+void Battlenet::Socket::OnClose()
+{
+ TC_LOG_TRACE("server.battlenet", "Battlenet::Socket::OnClose");
+}
+
+void Battlenet::Socket::Send(ServerPacket& packet)
+{
+ TC_LOG_TRACE("server.battlenet", "Battlenet::Socket::Send %s", packet.ToString().c_str());
+
+ packet.Write();
+
+ _crypt.EncryptSend(const_cast<uint8*>(packet.GetData()), packet.GetSize());
+ _socket.send(reinterpret_cast<char const*>(packet.GetData()), packet.GetSize());
+}
+
+inline void ReplaceResponse(Battlenet::ServerPacket** oldResponse, Battlenet::ServerPacket* newResponse)
+{
+ if (*oldResponse)
+ delete *oldResponse;
+
+ *oldResponse = newResponse;
+}
+
+bool Battlenet::Socket::HandlePasswordModule(BitStream* dataStream, ServerPacket** response)
+{
+ if (dataStream->GetSize() != 1 + 128 + 32 + 128)
+ {
+ AuthComplete* complete = new AuthComplete();
+ complete->SetAuthResult(AUTH_CORRUPTED_MODULE);
+ ReplaceResponse(response, complete);
+ return false;
+ }
+
+ if (dataStream->Read<uint8>(8) != 2) // State
+ {
+ AuthComplete* complete = new AuthComplete();
+ complete->SetAuthResult(AUTH_CORRUPTED_MODULE);
+ ReplaceResponse(response, complete);
+ return false;
+ }
+
+
+ BigNumber A, clientM1, clientChallenge;
+ A.SetBinary(dataStream->ReadBytes(128).get(), 128);
+ clientM1.SetBinary(dataStream->ReadBytes(32).get(), 32);
+ clientChallenge.SetBinary(dataStream->ReadBytes(128).get(), 128);
+
+ if (A.isZero())
+ {
+ AuthComplete* complete = new AuthComplete();
+ complete->SetAuthResult(AUTH_CORRUPTED_MODULE);
+ ReplaceResponse(response, complete);
+ return false;
+ }
+
+ SHA256Hash sha;
+ sha.UpdateBigNumbers(&A, &B, NULL);
+ sha.Finalize();
+
+ BigNumber u;
+ u.SetBinary(sha.GetDigest(), sha.GetLength());
+
+ BigNumber S = ((A * v.ModExp(u, N)) % N).ModExp(b, N);
+
+ uint8 S_bytes[128];
+ memcpy(S_bytes, S.AsByteArray(128).get(), 128);
+
+ uint8 part1[64];
+ uint8 part2[64];
+
+ for (int i = 0; i < 64; ++i)
+ {
+ part1[i] = S_bytes[i * 2];
+ part2[i] = S_bytes[i * 2 + 1];
+ }
+
+ SHA256Hash part1sha, part2sha;
+ part1sha.UpdateData(part1, 64);
+ part1sha.Finalize();
+ part2sha.UpdateData(part2, 64);
+ part2sha.Finalize();
+
+ uint8 sessionKey[SHA256_DIGEST_LENGTH * 2];
+ for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i)
+ {
+ sessionKey[i * 2] = part1sha.GetDigest()[i];
+ sessionKey[i * 2 + 1] = part2sha.GetDigest()[i];
+ }
+
+ K.SetBinary(sessionKey, SHA256_DIGEST_LENGTH * 2);
+
+ BigNumber M1;
+
+ uint8 hash[SHA256_DIGEST_LENGTH];
+ sha.Initialize();
+ sha.UpdateBigNumbers(&N, NULL);
+ sha.Finalize();
+ memcpy(hash, sha.GetDigest(), sha.GetLength());
+
+ sha.Initialize();
+ sha.UpdateBigNumbers(&g, NULL);
+ sha.Finalize();
+
+ for (int i = 0; i < sha.GetLength(); ++i)
+ hash[i] ^= sha.GetDigest()[i];
+
+ SHA256Hash shaI;
+ shaI.UpdateData(ByteArrayToHexStr(I.AsByteArray().get(), 32));
+ shaI.Finalize();
+
+ // Concat all variables for M1 hash
+ sha.Initialize();
+ sha.UpdateData(hash, SHA256_DIGEST_LENGTH);
+ sha.UpdateData(shaI.GetDigest(), shaI.GetLength());
+ sha.UpdateBigNumbers(&s, &A, &B, &K, NULL);
+ sha.Finalize();
+
+ M1.SetBinary(sha.GetDigest(), sha.GetLength());
+
+ if (memcmp(M1.AsByteArray().get(), clientM1.AsByteArray().get(), 32))
+ {
+ AuthComplete* complete = new AuthComplete();
+ complete->SetAuthResult(AUTH_UNKNOWN_ACCOUNT);
+ ReplaceResponse(response, complete);
+ return false;
+ }
+
+ uint64 numAccounts = 0;
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_GAME_ACCOUNTS);
+ stmt->setUInt32(0, _accountId);
+ PreparedQueryResult result = LoginDatabase.Query(stmt);
+ if (result)
+ numAccounts = result->GetRowCount();
+
+ if (!numAccounts)
+ {
+ AuthComplete* noAccounts = new AuthComplete();
+ noAccounts->SetAuthResult(LOGIN_NO_GAME_ACCOUNT);
+ ReplaceResponse(response, noAccounts);
+ return false;
+ }
+
+ Field* fields = result->Fetch();
+
+ //set expired game account bans to inactive
+ LoginDatabase.DirectExecute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS));
+
+ BigNumber M;
+ sha.Initialize();
+ sha.UpdateBigNumbers(&A, &M1, &K, NULL);
+ sha.Finalize();
+ M.SetBinary(sha.GetDigest(), sha.GetLength());
+
+ BigNumber serverProof;
+ serverProof.SetRand(128 * 8); // just send garbage, server signature check is patched out in client
+
+ BitStream stream;
+ ModuleInfo* password = sBattlenetMgr->CreateModule(_os, "Password");
+ uint8 state = 3;
+
+ stream.WriteBytes(&state, 1);
+ stream.WriteBytes(M.AsByteArray(32).get(), 32);
+ stream.WriteBytes(serverProof.AsByteArray(128).get(), 128);
+
+ password->DataSize = stream.GetSize();
+ password->Data = new uint8[password->DataSize];
+ memcpy(password->Data, stream.GetBuffer(), password->DataSize);
+
+ ProofRequest* request = new ProofRequest();
+ request->Modules.push_back(password);
+ if (numAccounts > 1)
+ {
+ BitStream accounts;
+ state = 0;
+ accounts.WriteBytes(&state, 1);
+ accounts.Write(numAccounts, 8);
+ do
+ {
+ fields = result->Fetch();
+ std::ostringstream name;
+ name << "WoW" << uint32(fields[0].GetUInt8());
+ accounts.Write(2, 8);
+ accounts.WriteString(name.str(), 8);
+ } while (result->NextRow());
+
+ ModuleInfo* selectGameAccount = sBattlenetMgr->CreateModule(_os, "SelectGameAccount");
+ selectGameAccount->DataSize = accounts.GetSize();
+ selectGameAccount->Data = new uint8[selectGameAccount->DataSize];
+ memcpy(selectGameAccount->Data, accounts.GetBuffer(), selectGameAccount->DataSize);
+ request->Modules.push_back(selectGameAccount);
+ _modulesWaitingForData.push(MODULE_SELECT_GAME_ACCOUNT);
+ }
+ else
+ {
+ if (fields[4].GetBool())
+ {
+ delete request;
+
+ AuthComplete* complete = new AuthComplete();
+ if (fields[2].GetUInt32() == fields[3].GetUInt32())
+ {
+ complete->SetAuthResult(LOGIN_BANNED);
+ TC_LOG_DEBUG("server.battlenet", "'%s:%d' [Battlenet::AuthChallenge] Banned account %s tried to login!", _socket.getRemoteAddress().c_str(), _socket.getRemotePort(), _accountName.c_str());
+ }
+ else
+ {
+ complete->SetAuthResult(LOGIN_SUSPENDED);
+ TC_LOG_DEBUG("server.battlenet", "'%s:%d' [Battlenet::AuthChallenge] Temporarily banned account %s tried to login!", _socket.getRemoteAddress().c_str(), _socket.getRemotePort(), _accountName.c_str());
+ }
+
+ ReplaceResponse(response, complete);
+ return false;
+ }
+
+ _gameAccountId = fields[1].GetUInt32();
+ _gameAccountIndex = fields[0].GetUInt8();
+
+ request->Modules.push_back(sBattlenetMgr->CreateModule(_os, "RiskFingerprint"));
+ _modulesWaitingForData.push(MODULE_RISK_FINGERPRINT);
+ }
+
+ ReplaceResponse(response, request);
+ return true;
+}
+
+bool Battlenet::Socket::HandleSelectGameAccountModule(BitStream* dataStream, ServerPacket** response)
+{
+ if (dataStream->Read<uint8>(8) != 1)
+ {
+ AuthComplete* complete = new AuthComplete();
+ complete->SetAuthResult(AUTH_CORRUPTED_MODULE);
+ ReplaceResponse(response, complete);
+ return false;
+ }
+
+ dataStream->Read<uint8>(8);
+ std::string account = dataStream->ReadString(8);
+ if (account.length() < 4)
+ {
+ AuthComplete* complete = new AuthComplete();
+ complete->SetAuthResult(LOGIN_NO_GAME_ACCOUNT);
+ ReplaceResponse(response, complete);
+ return false;
+ }
+
+ uint8 accountIndex = atol(account.substr(3).c_str());
+
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_GAME_ACCOUNT);
+ stmt->setUInt8(0, accountIndex);
+ stmt->setUInt32(1, _accountId);
+ PreparedQueryResult result = LoginDatabase.Query(stmt);
+ if (!result)
+ {
+ AuthComplete* complete = new AuthComplete();
+ complete->SetAuthResult(LOGIN_NO_GAME_ACCOUNT);
+ ReplaceResponse(response, complete);
+ return false;
+ }
+
+ Field* fields = result->Fetch();
+ if (fields[3].GetBool())
+ {
+ AuthComplete* complete = new AuthComplete();
+ if (fields[1].GetUInt32() == fields[2].GetUInt32())
+ {
+ complete->SetAuthResult(LOGIN_BANNED);
+ TC_LOG_DEBUG("server.battlenet", "'%s:%d' [Battlenet::SelectGameAccount] Banned account %s tried to login!", _socket.getRemoteAddress().c_str(), _socket.getRemotePort(), _accountName.c_str());
+ }
+ else
+ {
+ complete->SetAuthResult(LOGIN_SUSPENDED);
+ TC_LOG_DEBUG("server.battlenet", "'%s:%d' [Battlenet::SelectGameAccount] Temporarily banned account %s tried to login!", _socket.getRemoteAddress().c_str(), _socket.getRemotePort(), _accountName.c_str());
+ }
+
+ ReplaceResponse(response, complete);
+ return false;
+ }
+
+ _gameAccountId = fields[0].GetUInt32();
+ _gameAccountIndex = accountIndex;
+
+ ProofRequest* request = new ProofRequest();
+ request->Modules.push_back(sBattlenetMgr->CreateModule(_os, "RiskFingerprint"));
+ ReplaceResponse(response, request);
+
+ _modulesWaitingForData.push(MODULE_RISK_FINGERPRINT);
+ return true;
+}
+
+bool Battlenet::Socket::HandleRiskFingerprintModule(BitStream* dataStream, ServerPacket** response)
+{
+ AuthComplete* complete = new AuthComplete();
+ if (dataStream->Read<uint8>(8) == 1)
+ {
+ std::ostringstream str;
+ str << _accountId << "#" << uint32(_gameAccountIndex);
+
+ complete->AccountId = _accountId;
+ complete->GameAccountName = str.str();
+ complete->GameAccountFlags = GAMEACCOUNT_FLAG_PROPASS_LOCK;
+
+ SQLTransaction trans = LoginDatabase.BeginTransaction();
+
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_LAST_LOGIN_INFO);
+ stmt->setString(0, _socket.getRemoteAddress());
+ stmt->setUInt8(1, GetLocaleByName(_locale));
+ stmt->setString(2, _os);
+ stmt->setUInt32(3, _accountId);
+ trans->Append(stmt);
+
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_SESSION_KEY);
+ stmt->setString(0, K.AsHexStr());
+ stmt->setUInt32(1, _accountId);
+ trans->Append(stmt);
+
+ LoginDatabase.CommitTransaction(trans);
+
+ _authed = true;
+ }
+ else
+ complete->SetAuthResult(AUTH_BAD_VERSION_HASH);
+
+ ReplaceResponse(response, complete);
+ return true;
+}
+
+bool Battlenet::Socket::HandleResumeModule(BitStream* dataStream, ServerPacket** response)
+{
+ if (dataStream->Read<uint8>(8) != 1)
+ {
+ AuthResume* complete = new AuthResume();
+ complete->SetAuthResult(AUTH_CORRUPTED_MODULE);
+ ReplaceResponse(response, complete);
+ return false;
+ }
+
+ static uint8 const ResumeClient = 0;
+ static uint8 const ResumeServer = 1;
+
+ ACE_Auto_Array_Ptr<uint8>&& clientChallenge = dataStream->ReadBytes(16);
+ ACE_Auto_Array_Ptr<uint8>&& clientProof = dataStream->ReadBytes(32);
+ ACE_Auto_Array_Ptr<uint8>&& serverChallenge = _reconnectProof.AsByteArray();
+ ACE_Auto_Array_Ptr<uint8>&& sessionKey = K.AsByteArray();
+
+ HmacSha256 clientPart(64, sessionKey.get());
+ clientPart.UpdateData(&ResumeClient, 1);
+ clientPart.UpdateData(clientChallenge.get(), 16);
+ clientPart.UpdateData(serverChallenge.get(), 16);
+ clientPart.Finalize();
+
+ HmacSha256 serverPart(64, sessionKey.get());
+ serverPart.UpdateData(&ResumeServer, 1);
+ serverPart.UpdateData(serverChallenge.get(), 16);
+ serverPart.UpdateData(clientChallenge.get(), 16);
+ serverPart.Finalize();
+
+ uint8 newSessionKey[64];
+ memcpy(&newSessionKey[0], clientPart.GetDigest(), clientPart.GetLength());
+ memcpy(&newSessionKey[32], serverPart.GetDigest(), serverPart.GetLength());
+
+ K.SetBinary(newSessionKey, 64);
+
+ HmacSha256 proof(64, newSessionKey);
+ proof.UpdateData(&ResumeClient, 1);
+ proof.UpdateData(clientChallenge.get(), 16);
+ proof.UpdateData(serverChallenge.get(), 16);
+ proof.Finalize();
+
+ if (memcmp(proof.GetDigest(), clientProof.get(), serverPart.GetLength()))
+ {
+ TC_LOG_DEBUG("server.battlenet", "[Battlenet::Resume] Invalid proof!");
+ AuthResume* result = new AuthResume();
+ result->SetAuthResult(AUTH_UNKNOWN_ACCOUNT);
+ ReplaceResponse(response, result);
+ return false;
+ }
+
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_SESSION_KEY);
+ stmt->setString(0, K.AsHexStr());
+ stmt->setUInt32(1, _accountId);
+ LoginDatabase.Execute(stmt);
+
+ HmacSha256 serverProof(64, newSessionKey);
+ serverProof.UpdateData(&ResumeServer, 1);
+ serverProof.UpdateData(serverChallenge.get(), 16);
+ serverProof.UpdateData(clientChallenge.get(), 16);
+ serverProof.Finalize();
+
+ ModuleInfo* resume = sBattlenetMgr->CreateModule(_os, "Resume");
+
+ BitStream resumeData;
+ uint8 state = 2;
+ resumeData.WriteBytes(&state, 1);
+ resumeData.WriteBytes(serverProof.GetDigest(), serverProof.GetLength());
+
+ resume->DataSize = resumeData.GetSize();
+ resume->Data = new uint8[resume->DataSize];
+ memcpy(resume->Data, resumeData.GetBuffer(), resume->DataSize);
+
+ AuthResume* result = new AuthResume();
+ result->Modules.push_back(resume);
+ ReplaceResponse(response, result);
+ _authed = true;
+ return true;
+}
+
+bool Battlenet::Socket::UnhandledModule(BitStream* /*dataStream*/, ServerPacket** response)
+{
+ AuthComplete* complete = new AuthComplete();
+ complete->SetAuthResult(AUTH_CORRUPTED_MODULE);
+ ReplaceResponse(response, complete);
+ return false;
+}
diff --git a/src/server/authserver/Server/BattlenetSocket.h b/src/server/authserver/Server/BattlenetSocket.h
new file mode 100644
index 00000000000..ee399e26b09
--- /dev/null
+++ b/src/server/authserver/Server/BattlenetSocket.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _BATTLENETSOCKET_H
+#define _BATTLENETSOCKET_H
+
+#include "RealmSocket.h"
+#include "BattlenetPackets.h"
+#include "BattlenetPacketCrypt.h"
+#include "BigNumber.h"
+
+class ACE_INET_Addr;
+
+namespace Battlenet
+{
+ struct PacketHeader;
+ class BitStream;
+
+ enum ModuleType
+ {
+ MODULE_PASSWORD,
+ MODULE_TOKEN,
+ MODULE_THUMBPRINT,
+ MODULE_SELECT_GAME_ACCOUNT,
+ MODULE_RISK_FINGERPRINT,
+ MODULE_RESUME,
+
+ MODULE_COUNT
+ };
+
+ class Socket : public RealmSocket::Session
+ {
+ public:
+ static uint32 const SRP6_V_Size;
+ static uint32 const SRP6_S_Size;
+
+ explicit Socket(RealmSocket& socket);
+
+ typedef bool(Socket::*PacketHandler)(PacketHeader& socket, BitStream& packet);
+
+ // Auth
+ bool HandleAuthChallenge(PacketHeader& header, BitStream& packet);
+ bool HandleAuthReconnect(PacketHeader& header, BitStream& packet);
+ bool HandleAuthProofResponse(PacketHeader& header, BitStream& packet);
+
+ // Creep
+ bool HandlePing(PacketHeader& header, BitStream& packet);
+ bool HandleEnableEncryption(PacketHeader& header, BitStream& packet);
+ bool HandleDisconnect(PacketHeader& header, BitStream& packet);
+
+ // WoW
+ bool HandleRealmUpdateSubscribe(PacketHeader& header, BitStream& packet);
+ bool HandleRealmJoinRequest(PacketHeader& header, BitStream& packet);
+
+ void OnRead() override;
+ void OnAccept() override;
+ void OnClose() override;
+
+ void Send(ServerPacket& packet);
+
+ private:
+ void _SetVSFields(std::string const& rI);
+
+ typedef bool(Socket::*ModuleHandler)(BitStream* dataStream, ServerPacket** response);
+ static ModuleHandler const ModuleHandlers[MODULE_COUNT];
+
+ bool HandlePasswordModule(BitStream* dataStream, ServerPacket** response);
+ bool HandleSelectGameAccountModule(BitStream* dataStream, ServerPacket** response);
+ bool HandleRiskFingerprintModule(BitStream* dataStream, ServerPacket** response);
+ bool HandleResumeModule(BitStream* dataStream, ServerPacket** response);
+ bool UnhandledModule(BitStream* dataStream, ServerPacket** response);
+
+ RealmSocket& _socket;
+
+ uint32 _accountId;
+ std::string _accountName;
+ std::string _locale;
+ std::string _os;
+ uint32 _build;
+ uint32 _gameAccountId;
+ uint8 _gameAccountIndex;
+ AccountTypes _accountSecurityLevel;
+
+ BigNumber N;
+ BigNumber g;
+ BigNumber k;
+
+ BigNumber I;
+ BigNumber s;
+ BigNumber v;
+
+ BigNumber b;
+ BigNumber B;
+ BigNumber K; // session key
+
+ BigNumber _reconnectProof;
+
+ std::queue<ModuleType> _modulesWaitingForData;
+
+ PacketCrypt _crypt;
+ bool _authed;
+ };
+
+}
+
+#endif // _BATTLENETSOCKET_H
diff --git a/src/server/authserver/Server/RealmAcceptor.h b/src/server/authserver/Server/RealmAcceptor.h
index e89135c4896..2089b0c7a22 100644
--- a/src/server/authserver/Server/RealmAcceptor.h
+++ b/src/server/authserver/Server/RealmAcceptor.h
@@ -24,7 +24,9 @@
#include "RealmSocket.h"
#include "AuthSocket.h"
+#include "BattlenetSocket.h"
+template<class LoginType>
class RealmAcceptor : public ACE_Acceptor<RealmSocket, ACE_SOCK_Acceptor>
{
public:
@@ -42,7 +44,7 @@ protected:
ACE_NEW_RETURN(sh, RealmSocket, -1);
sh->reactor(reactor());
- sh->set_session(new AuthSocket(*sh));
+ sh->set_session(new LoginType(*sh));
return 0;
}
diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp
index 773e169e5c2..59a8f046a6b 100644
--- a/src/server/game/Accounts/AccountMgr.cpp
+++ b/src/server/game/Accounts/AccountMgr.cpp
@@ -32,17 +32,17 @@ AccountMgr::~AccountMgr()
ClearRBAC();
}
-AccountOpResult AccountMgr::CreateAccount(std::string username, std::string password, std::string email = "")
+AccountOpResult AccountMgr::CreateAccount(std::string username, std::string password, std::string email /*= ""*/)
{
if (utf8length(username) > MAX_ACCOUNT_STR)
- return AOR_NAME_TOO_LONG; // username's too long
+ return AccountOpResult::AOR_NAME_TOO_LONG; // username's too long
- normalizeString(username);
- normalizeString(password);
- normalizeString(email);
+ Utf8ToUpperOnlyLatin(username);
+ Utf8ToUpperOnlyLatin(password);
+ Utf8ToUpperOnlyLatin(email);
if (GetId(username))
- return AOR_NAME_ALREADY_EXIST; // username does already exist
+ return AccountOpResult::AOR_NAME_ALREADY_EXIST; // username does already exist
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT);
@@ -56,7 +56,7 @@ AccountOpResult AccountMgr::CreateAccount(std::string username, std::string pass
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_REALM_CHARACTERS_INIT);
LoginDatabase.Execute(stmt);
- return AOR_OK; // everything's fine
+ return AccountOpResult::AOR_OK; // everything's fine
}
AccountOpResult AccountMgr::DeleteAccount(uint32 accountId)
@@ -67,7 +67,7 @@ AccountOpResult AccountMgr::DeleteAccount(uint32 accountId)
PreparedQueryResult result = LoginDatabase.Query(stmt);
if (!result)
- return AOR_NAME_NOT_EXIST;
+ return AccountOpResult::AOR_NAME_NOT_EXIST;
// Obtain accounts characters
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID);
@@ -128,7 +128,7 @@ AccountOpResult AccountMgr::DeleteAccount(uint32 accountId)
LoginDatabase.CommitTransaction(trans);
- return AOR_OK;
+ return AccountOpResult::AOR_OK;
}
AccountOpResult AccountMgr::ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword)
@@ -139,16 +139,16 @@ AccountOpResult AccountMgr::ChangeUsername(uint32 accountId, std::string newUser
PreparedQueryResult result = LoginDatabase.Query(stmt);
if (!result)
- return AOR_NAME_NOT_EXIST;
+ return AccountOpResult::AOR_NAME_NOT_EXIST;
if (utf8length(newUsername) > MAX_ACCOUNT_STR)
- return AOR_NAME_TOO_LONG;
+ return AccountOpResult::AOR_NAME_TOO_LONG;
if (utf8length(newPassword) > MAX_ACCOUNT_STR)
- return AOR_PASS_TOO_LONG;
+ return AccountOpResult::AOR_PASS_TOO_LONG;
- normalizeString(newUsername);
- normalizeString(newPassword);
+ Utf8ToUpperOnlyLatin(newUsername);
+ Utf8ToUpperOnlyLatin(newPassword);
stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_USERNAME);
@@ -158,7 +158,7 @@ AccountOpResult AccountMgr::ChangeUsername(uint32 accountId, std::string newUser
LoginDatabase.Execute(stmt);
- return AOR_OK;
+ return AccountOpResult::AOR_OK;
}
AccountOpResult AccountMgr::ChangePassword(uint32 accountId, std::string newPassword)
@@ -166,13 +166,13 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accountId, std::string newPass
std::string username;
if (!GetName(accountId, username))
- return AOR_NAME_NOT_EXIST; // account doesn't exist
+ return AccountOpResult::AOR_NAME_NOT_EXIST; // account doesn't exist
if (utf8length(newPassword) > MAX_ACCOUNT_STR)
- return AOR_PASS_TOO_LONG;
+ return AccountOpResult::AOR_PASS_TOO_LONG;
- normalizeString(username);
- normalizeString(newPassword);
+ Utf8ToUpperOnlyLatin(username);
+ Utf8ToUpperOnlyLatin(newPassword);
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_PASSWORD);
@@ -189,7 +189,7 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accountId, std::string newPass
LoginDatabase.Execute(stmt);
- return AOR_OK;
+ return AccountOpResult::AOR_OK;
}
AccountOpResult AccountMgr::ChangeEmail(uint32 accountId, std::string newEmail)
@@ -197,13 +197,13 @@ AccountOpResult AccountMgr::ChangeEmail(uint32 accountId, std::string newEmail)
std::string username;
if (!GetName(accountId, username))
- return AOR_NAME_NOT_EXIST; // account doesn't exist
+ return AccountOpResult::AOR_NAME_NOT_EXIST; // account doesn't exist
if (utf8length(newEmail) > MAX_EMAIL_STR)
- return AOR_EMAIL_TOO_LONG;
+ return AccountOpResult::AOR_EMAIL_TOO_LONG;
- normalizeString(username);
- normalizeString(newEmail);
+ Utf8ToUpperOnlyLatin(username);
+ Utf8ToUpperOnlyLatin(newEmail);
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_EMAIL);
@@ -212,7 +212,7 @@ AccountOpResult AccountMgr::ChangeEmail(uint32 accountId, std::string newEmail)
LoginDatabase.Execute(stmt);
- return AOR_OK;
+ return AccountOpResult::AOR_OK;
}
AccountOpResult AccountMgr::ChangeRegEmail(uint32 accountId, std::string newEmail)
@@ -220,13 +220,13 @@ AccountOpResult AccountMgr::ChangeRegEmail(uint32 accountId, std::string newEmai
std::string username;
if (!GetName(accountId, username))
- return AOR_NAME_NOT_EXIST; // account doesn't exist
+ return AccountOpResult::AOR_NAME_NOT_EXIST; // account doesn't exist
if (utf8length(newEmail) > MAX_EMAIL_STR)
- return AOR_EMAIL_TOO_LONG;
+ return AccountOpResult::AOR_EMAIL_TOO_LONG;
- normalizeString(username);
- normalizeString(newEmail);
+ Utf8ToUpperOnlyLatin(username);
+ Utf8ToUpperOnlyLatin(newEmail);
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_REG_EMAIL);
@@ -235,7 +235,7 @@ AccountOpResult AccountMgr::ChangeRegEmail(uint32 accountId, std::string newEmai
LoginDatabase.Execute(stmt);
- return AOR_OK;
+ return AccountOpResult::AOR_OK;
}
uint32 AccountMgr::GetId(std::string const& username)
@@ -303,8 +303,8 @@ bool AccountMgr::CheckPassword(uint32 accountId, std::string password)
if (!GetName(accountId, username))
return false;
- normalizeString(username);
- normalizeString(password);
+ Utf8ToUpperOnlyLatin(username);
+ Utf8ToUpperOnlyLatin(password);
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD);
stmt->setUInt32(0, accountId);
@@ -322,8 +322,8 @@ bool AccountMgr::CheckEmail(uint32 accountId, std::string newEmail)
if (!GetEmail(accountId, oldEmail))
return false;
- normalizeString(oldEmail);
- normalizeString(newEmail);
+ Utf8ToUpperOnlyLatin(oldEmail);
+ Utf8ToUpperOnlyLatin(newEmail);
if (strcmp(oldEmail.c_str(), newEmail.c_str()) == 0)
return true;
@@ -341,19 +341,6 @@ uint32 AccountMgr::GetCharactersCount(uint32 accountId)
return (result) ? (*result)[0].GetUInt64() : 0;
}
-bool AccountMgr::normalizeString(std::string& utf8String)
-{
- wchar_t buffer[MAX_ACCOUNT_STR+1];
-
- size_t maxLength = MAX_ACCOUNT_STR;
- if (!Utf8toWStr(utf8String, buffer, maxLength))
- return false;
-
- std::transform(&buffer[0], buffer+maxLength, &buffer[0], wcharToUpperOnlyLatin);
-
- return WStrToUtf8(buffer, maxLength, utf8String);
-}
-
std::string AccountMgr::CalculateShaPassHash(std::string const& name, std::string const& password)
{
SHA1Hash sha;
diff --git a/src/server/game/Accounts/AccountMgr.h b/src/server/game/Accounts/AccountMgr.h
index b3012ace177..f39873f0ebf 100644
--- a/src/server/game/Accounts/AccountMgr.h
+++ b/src/server/game/Accounts/AccountMgr.h
@@ -22,7 +22,7 @@
#include "RBAC.h"
#include <ace/Singleton.h>
-enum AccountOpResult
+enum class AccountOpResult : uint8
{
AOR_OK,
AOR_NAME_TOO_LONG,
@@ -40,6 +40,7 @@ enum PasswordChangeSecurity
PW_RBAC
};
+#define MAX_PASS_STR 16
#define MAX_ACCOUNT_STR 16
#define MAX_EMAIL_STR 64
@@ -58,7 +59,7 @@ class AccountMgr
~AccountMgr();
public:
- AccountOpResult CreateAccount(std::string username, std::string password, std::string email);
+ AccountOpResult CreateAccount(std::string username, std::string password, std::string email = "");
static AccountOpResult DeleteAccount(uint32 accountId);
static AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword);
static AccountOpResult ChangePassword(uint32 accountId, std::string newPassword);
@@ -75,7 +76,6 @@ class AccountMgr
static uint32 GetCharactersCount(uint32 accountId);
static std::string CalculateShaPassHash(std::string const& name, std::string const& password);
- static bool normalizeString(std::string& utf8String);
static bool IsPlayerAccount(uint32 gmlevel);
static bool IsAdminAccount(uint32 gmlevel);
static bool IsConsoleAccount(uint32 gmlevel);
diff --git a/src/server/game/Accounts/BattlenetAccountMgr.cpp b/src/server/game/Accounts/BattlenetAccountMgr.cpp
new file mode 100644
index 00000000000..8b73ba19a97
--- /dev/null
+++ b/src/server/game/Accounts/BattlenetAccountMgr.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "AccountMgr.h"
+#include "BattlenetAccountMgr.h"
+#include "DatabaseEnv.h"
+#include "Util.h"
+#include "SHA256.h"
+
+AccountOpResult Battlenet::AccountMgr::CreateBattlenetAccount(std::string email, std::string password)
+{
+ if (utf8length(email) > 64)
+ return AccountOpResult::AOR_NAME_TOO_LONG;
+
+ Utf8ToUpperOnlyLatin(email);
+ Utf8ToUpperOnlyLatin(password);
+
+ if (GetId(email))
+ return AccountOpResult::AOR_NAME_ALREADY_EXIST;
+
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_ACCOUNT);
+ stmt->setString(0, email);
+ stmt->setString(1, CalculateShaPassHash(email, password));
+ LoginDatabase.Execute(stmt);
+
+ return AccountOpResult::AOR_OK;
+}
+
+AccountOpResult Battlenet::AccountMgr::ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword)
+{
+ // Check if accounts exists
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ACCOUNT_EMAIL_BY_ID);
+ stmt->setUInt32(0, accountId);
+ PreparedQueryResult result = LoginDatabase.Query(stmt);
+
+ if (!result)
+ return AccountOpResult::AOR_NAME_NOT_EXIST;
+
+ Utf8ToUpperOnlyLatin(newUsername);
+ if (utf8length(newUsername) > MAX_ACCOUNT_STR)
+ return AccountOpResult::AOR_NAME_TOO_LONG;
+
+ Utf8ToUpperOnlyLatin(newPassword);
+ if (utf8length(newPassword) > MAX_PASS_STR)
+ return AccountOpResult::AOR_PASS_TOO_LONG;
+
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_PASSWORD);
+ stmt->setString(0, newUsername);
+ stmt->setString(1, CalculateShaPassHash(newUsername, newPassword));
+ stmt->setUInt32(2, accountId);
+ LoginDatabase.Execute(stmt);
+
+ return AccountOpResult::AOR_OK;
+}
+
+AccountOpResult Battlenet::AccountMgr::ChangePassword(uint32 accountId, std::string newPassword)
+{
+ std::string username;
+ if (!GetName(accountId, username))
+ return AccountOpResult::AOR_NAME_NOT_EXIST; // account doesn't exist
+
+ Utf8ToUpperOnlyLatin(username);
+ Utf8ToUpperOnlyLatin(newPassword);
+ if (utf8length(newPassword) > MAX_PASS_STR)
+ return AccountOpResult::AOR_PASS_TOO_LONG;
+
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_PASSWORD);
+ stmt->setString(0, username);
+ stmt->setString(1, CalculateShaPassHash(username, newPassword));
+ stmt->setUInt32(2, accountId);
+ LoginDatabase.Execute(stmt);
+
+ return AccountOpResult::AOR_OK;
+}
+
+uint32 Battlenet::AccountMgr::GetId(std::string const& username)
+{
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ACCOUNT_ID_BY_EMAIL);
+ stmt->setString(0, username);
+ if (PreparedQueryResult result = LoginDatabase.Query(stmt))
+ return (*result)[0].GetUInt32();
+
+ return 0;
+}
+
+bool Battlenet::AccountMgr::GetName(uint32 accountId, std::string& name)
+{
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ACCOUNT_EMAIL_BY_ID);
+ stmt->setUInt32(0, accountId);
+ if (PreparedQueryResult result = LoginDatabase.Query(stmt))
+ {
+ name = (*result)[0].GetString();
+ return true;
+ }
+
+ return false;
+}
+
+bool Battlenet::AccountMgr::CheckPassword(uint32 accountId, std::string password)
+{
+ std::string username;
+
+ if (!GetName(accountId, username))
+ return false;
+
+ Utf8ToUpperOnlyLatin(username);
+ Utf8ToUpperOnlyLatin(password);
+
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_CHECK_PASSWORD);
+ stmt->setUInt32(0, accountId);
+ stmt->setString(1, CalculateShaPassHash(username, password));
+ PreparedQueryResult result = LoginDatabase.Query(stmt);
+
+ return !result.null();
+}
+
+std::string Battlenet::AccountMgr::CalculateShaPassHash(std::string const& name, std::string const& password)
+{
+ SHA256Hash email;
+ email.UpdateData(name);
+ email.Finalize();
+
+ SHA256Hash sha;
+ sha.UpdateData(ByteArrayToHexStr(email.GetDigest(), email.GetLength()));
+ sha.UpdateData(":");
+ sha.UpdateData(password);
+ sha.Finalize();
+
+ return ByteArrayToHexStr(sha.GetDigest(), sha.GetLength(), true);
+}
diff --git a/src/server/game/Accounts/BattlenetAccountMgr.h b/src/server/game/Accounts/BattlenetAccountMgr.h
new file mode 100644
index 00000000000..07191d24313
--- /dev/null
+++ b/src/server/game/Accounts/BattlenetAccountMgr.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BattlenetAccountMgr_h__
+#define BattlenetAccountMgr_h__
+
+#include "Define.h"
+#include <string>
+#include <ace/Singleton.h>
+
+enum class AccountOpResult : uint8;
+
+#define MAX_BNET_EMAIL_STR 320
+
+namespace Battlenet
+{
+ namespace AccountMgr
+ {
+ AccountOpResult CreateBattlenetAccount(std::string email, std::string password);
+ AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword);
+ AccountOpResult ChangePassword(uint32 accountId, std::string newPassword);
+ bool CheckPassword(uint32 accountId, std::string password);
+
+ uint32 GetId(std::string const& username);
+ bool GetName(uint32 accountId, std::string& name);
+
+ std::string CalculateShaPassHash(std::string const& name, std::string const& password);
+ }
+}
+
+#endif // BattlenetAccountMgr_h__
diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp
index 737a2f8cbf1..1a93e7ae1d5 100644
--- a/src/server/game/Server/WorldSocket.cpp
+++ b/src/server/game/Server/WorldSocket.cpp
@@ -856,9 +856,20 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
// Get the account information from the realmd database
// 0 1 2 3 4 5 6 7 8
// SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ?
- PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME);
-
- stmt->setString(0, account);
+ size_t hashPos = account.find_last_of('#');
+ PreparedStatement* stmt;
+ if (hashPos != std::string::npos)
+ {
+ Tokenizer tokens(account, '#', 2);
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_BNET);
+ stmt->setUInt32(0, atol(tokens[0]));
+ stmt->setUInt8(1, atol(tokens[1]));
+ }
+ else
+ {
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME);
+ stmt->setString(0, account);
+ }
PreparedQueryResult result = LoginDatabase.Query(stmt);
diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h
index 4ccacd05c7e..306a50c083a 100644
--- a/src/server/game/Server/WorldSocket.h
+++ b/src/server/game/Server/WorldSocket.h
@@ -39,7 +39,7 @@
#endif /* ACE_LACKS_PRAGMA_ONCE */
#include "Common.h"
-#include "AuthCrypt.h"
+#include "WorldPacketCrypt.h"
class ACE_Message_Block;
class WorldPacket;
@@ -176,7 +176,7 @@ class WorldSocket : public WorldHandler
std::string m_Address;
/// Class used for managing encryption of the headers
- AuthCrypt m_Crypt;
+ WorldPacketCrypt m_Crypt;
/// Mutex lock to protect m_Session
LockType m_SessionLock;
diff --git a/src/server/game/Warden/WardenWin.cpp b/src/server/game/Warden/WardenWin.cpp
index 18bf2897358..3428708ed69 100644
--- a/src/server/game/Warden/WardenWin.cpp
+++ b/src/server/game/Warden/WardenWin.cpp
@@ -16,7 +16,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "Cryptography/HMACSHA1.h"
+#include "Cryptography/HmacHash.h"
#include "Cryptography/WardenKeyGeneration.h"
#include "Common.h"
#include "WorldPacket.h"
@@ -283,7 +283,7 @@ void WardenWin::RequestData()
{
uint32 seed = static_cast<uint32>(rand32());
buff << uint32(seed);
- HmacHash hmac(4, (uint8*)&seed);
+ HmacSha1 hmac(4, (uint8*)&seed);
hmac.UpdateData(wd->Str);
hmac.Finalize();
buff.append(hmac.GetDigest(), hmac.GetLength());
diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp
index 3c9714ca55a..a438cd995e7 100644
--- a/src/server/scripts/Commands/cs_account.cpp
+++ b/src/server/scripts/Commands/cs_account.cpp
@@ -23,6 +23,7 @@ Category: commandscripts
EndScriptData */
#include "AccountMgr.h"
+#include "BattlenetAccountMgr.h"
#include "Chat.h"
#include "Language.h"
#include "Player.h"
@@ -126,10 +127,15 @@ public:
if (!accountName || !password)
return false;
- AccountOpResult result = sAccountMgr->CreateAccount(std::string(accountName), std::string(password), email);
+ AccountOpResult result;
+ if (strchr(accountName, '@'))
+ result = Battlenet::AccountMgr::CreateBattlenetAccount(std::string(accountName), std::string(password));
+ else
+ result = sAccountMgr->CreateAccount(std::string(accountName), std::string(password), email);
+
switch (result)
{
- case AOR_OK:
+ case AccountOpResult::AOR_OK:
handler->PSendSysMessage(LANG_ACCOUNT_CREATED, accountName);
if (handler->GetSession())
{
@@ -139,15 +145,15 @@ public:
accountName, email.c_str());
}
break;
- case AOR_NAME_TOO_LONG:
+ case AccountOpResult::AOR_NAME_TOO_LONG:
handler->SendSysMessage(LANG_ACCOUNT_TOO_LONG);
handler->SetSentErrorMessage(true);
return false;
- case AOR_NAME_ALREADY_EXIST:
+ case AccountOpResult::AOR_NAME_ALREADY_EXIST:
handler->SendSysMessage(LANG_ACCOUNT_ALREADY_EXIST);
handler->SetSentErrorMessage(true);
return false;
- case AOR_DB_INTERNAL_ERROR:
+ case AccountOpResult::AOR_DB_INTERNAL_ERROR:
handler->PSendSysMessage(LANG_ACCOUNT_NOT_CREATED_SQL_ERROR, accountName);
handler->SetSentErrorMessage(true);
return false;
@@ -173,7 +179,7 @@ public:
return false;
std::string accountName = account;
- if (!AccountMgr::normalizeString(accountName))
+ if (!Utf8ToUpperOnlyLatin(accountName))
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
@@ -197,14 +203,14 @@ public:
AccountOpResult result = AccountMgr::DeleteAccount(accountId);
switch (result)
{
- case AOR_OK:
+ case AccountOpResult::AOR_OK:
handler->PSendSysMessage(LANG_ACCOUNT_DELETED, accountName.c_str());
break;
- case AOR_NAME_NOT_EXIST:
+ case AccountOpResult::AOR_NAME_NOT_EXIST:
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
- case AOR_DB_INTERNAL_ERROR:
+ case AccountOpResult::AOR_DB_INTERNAL_ERROR:
handler->PSendSysMessage(LANG_ACCOUNT_NOT_DELETED_SQL_ERROR, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
@@ -416,14 +422,14 @@ public:
AccountOpResult result = AccountMgr::ChangeEmail(handler->GetSession()->GetAccountId(), std::string(email));
switch (result)
{
- case AOR_OK:
+ case AccountOpResult::AOR_OK:
handler->SendSysMessage(LANG_COMMAND_EMAIL);
TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Changed Email from [%s] to [%s].",
handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(),
handler->GetSession()->GetPlayer()->GetName().c_str(), handler->GetSession()->GetPlayer()->GetGUIDLow(),
oldEmail, email);
break;
- case AOR_EMAIL_TOO_LONG:
+ case AccountOpResult::AOR_EMAIL_TOO_LONG:
handler->SendSysMessage(LANG_EMAIL_TOO_LONG);
handler->SetSentErrorMessage(true);
return false;
@@ -501,13 +507,13 @@ public:
AccountOpResult result = AccountMgr::ChangePassword(handler->GetSession()->GetAccountId(), std::string(newPassword));
switch (result)
{
- case AOR_OK:
+ case AccountOpResult::AOR_OK:
handler->SendSysMessage(LANG_COMMAND_PASSWORD);
TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Changed Password.",
handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(),
handler->GetSession()->GetPlayer()->GetName().c_str(), handler->GetSession()->GetPlayer()->GetGUIDLow());
break;
- case AOR_PASS_TOO_LONG:
+ case AccountOpResult::AOR_PASS_TOO_LONG:
handler->SendSysMessage(LANG_PASSWORD_TOO_LONG);
handler->SetSentErrorMessage(true);
return false;
@@ -586,7 +592,7 @@ public:
{
///- Convert Account name to Upper Format
accountName = account;
- if (!AccountMgr::normalizeString(accountName))
+ if (!Utf8ToUpperOnlyLatin(accountName))
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
@@ -656,7 +662,7 @@ public:
if (isAccountNameGiven)
{
targetAccountName = arg1;
- if (!AccountMgr::normalizeString(targetAccountName))
+ if (!Utf8ToUpperOnlyLatin(targetAccountName))
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, targetAccountName.c_str());
handler->SetSentErrorMessage(true);
@@ -744,7 +750,7 @@ public:
return false;
std::string accountName = account;
- if (!AccountMgr::normalizeString(accountName))
+ if (!Utf8ToUpperOnlyLatin(accountName))
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
@@ -775,14 +781,14 @@ public:
switch (result)
{
- case AOR_OK:
+ case AccountOpResult::AOR_OK:
handler->SendSysMessage(LANG_COMMAND_PASSWORD);
break;
- case AOR_NAME_NOT_EXIST:
+ case AccountOpResult::AOR_NAME_NOT_EXIST:
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
- case AOR_PASS_TOO_LONG:
+ case AccountOpResult::AOR_PASS_TOO_LONG:
handler->SendSysMessage(LANG_PASSWORD_TOO_LONG);
handler->SetSentErrorMessage(true);
return false;
@@ -813,7 +819,7 @@ public:
}
std::string accountName = account;
- if (!AccountMgr::normalizeString(accountName))
+ if (!Utf8ToUpperOnlyLatin(accountName))
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
@@ -843,16 +849,16 @@ public:
AccountOpResult result = AccountMgr::ChangeEmail(targetAccountId, email);
switch (result)
{
- case AOR_OK:
+ case AccountOpResult::AOR_OK:
handler->SendSysMessage(LANG_COMMAND_EMAIL);
TC_LOG_INFO("entities.player.character", "ChangeEmail: Account %s [Id: %u] had it's email changed to %s.",
accountName.c_str(), targetAccountId, email);
break;
- case AOR_NAME_NOT_EXIST:
+ case AccountOpResult::AOR_NAME_NOT_EXIST:
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
- case AOR_EMAIL_TOO_LONG:
+ case AccountOpResult::AOR_EMAIL_TOO_LONG:
handler->SendSysMessage(LANG_EMAIL_TOO_LONG);
handler->SetSentErrorMessage(true);
return false;
@@ -889,7 +895,7 @@ public:
}
std::string accountName = account;
- if (!AccountMgr::normalizeString(accountName))
+ if (!Utf8ToUpperOnlyLatin(accountName))
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
@@ -919,16 +925,16 @@ public:
AccountOpResult result = AccountMgr::ChangeRegEmail(targetAccountId, email);
switch (result)
{
- case AOR_OK:
+ case AccountOpResult::AOR_OK:
handler->SendSysMessage(LANG_COMMAND_EMAIL);
TC_LOG_INFO("entities.player.character", "ChangeRegEmail: Account %s [Id: %u] had it's Registration Email changed to %s.",
accountName.c_str(), targetAccountId, email);
break;
- case AOR_NAME_NOT_EXIST:
+ case AccountOpResult::AOR_NAME_NOT_EXIST:
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
- case AOR_EMAIL_TOO_LONG:
+ case AccountOpResult::AOR_EMAIL_TOO_LONG:
handler->SendSysMessage(LANG_EMAIL_TOO_LONG);
handler->SetSentErrorMessage(true);
return false;
diff --git a/src/server/scripts/Commands/cs_ban.cpp b/src/server/scripts/Commands/cs_ban.cpp
index afaf5f651bc..ab962b69869 100644
--- a/src/server/scripts/Commands/cs_ban.cpp
+++ b/src/server/scripts/Commands/cs_ban.cpp
@@ -174,7 +174,7 @@ public:
switch (mode)
{
case BAN_ACCOUNT:
- if (!AccountMgr::normalizeString(nameOrIP))
+ if (!Utf8ToUpperOnlyLatin(nameOrIP))
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, nameOrIP.c_str());
handler->SetSentErrorMessage(true);
@@ -245,7 +245,7 @@ public:
return false;
std::string accountName = nameStr;
- if (!AccountMgr::normalizeString(accountName))
+ if (!Utf8ToUpperOnlyLatin(accountName))
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
@@ -710,7 +710,7 @@ public:
switch (mode)
{
case BAN_ACCOUNT:
- if (!AccountMgr::normalizeString(nameOrIP))
+ if (!Utf8ToUpperOnlyLatin(nameOrIP))
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, nameOrIP.c_str());
handler->SetSentErrorMessage(true);
diff --git a/src/server/scripts/Commands/cs_character.cpp b/src/server/scripts/Commands/cs_character.cpp
index ffdc9deeae3..0c6d495a360 100644
--- a/src/server/scripts/Commands/cs_character.cpp
+++ b/src/server/scripts/Commands/cs_character.cpp
@@ -858,7 +858,7 @@ public:
return false;
std::string accountName = accountStr;
- if (!AccountMgr::normalizeString(accountName))
+ if (!Utf8ToUpperOnlyLatin(accountName))
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
diff --git a/src/server/scripts/Commands/cs_lookup.cpp b/src/server/scripts/Commands/cs_lookup.cpp
index 15d065f127e..64829a46a05 100644
--- a/src/server/scripts/Commands/cs_lookup.cpp
+++ b/src/server/scripts/Commands/cs_lookup.cpp
@@ -1218,8 +1218,7 @@ public:
char* limitStr = strtok(NULL, " ");
int32 limit = limitStr ? atoi(limitStr) : -1;
- if (!AccountMgr::normalizeString
- (account))
+ if (!Utf8ToUpperOnlyLatin(account))
return false;
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME);
diff --git a/src/server/scripts/Commands/cs_rbac.cpp b/src/server/scripts/Commands/cs_rbac.cpp
index 47fa01837f6..080e868a924 100644
--- a/src/server/scripts/Commands/cs_rbac.cpp
+++ b/src/server/scripts/Commands/cs_rbac.cpp
@@ -142,7 +142,7 @@ public:
{
accountName = param1;
- if (AccountMgr::normalizeString(accountName))
+ if (Utf8ToUpperOnlyLatin(accountName))
accountId = AccountMgr::GetId(accountName);
if (!accountId)
diff --git a/src/server/shared/Cryptography/Authentication/PacketCrypt.cpp b/src/server/shared/Cryptography/Authentication/PacketCrypt.cpp
new file mode 100644
index 00000000000..7fac311b8a2
--- /dev/null
+++ b/src/server/shared/Cryptography/Authentication/PacketCrypt.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "PacketCrypt.h"
+
+PacketCrypt::PacketCrypt(uint32 rc4InitSize)
+ : _clientDecrypt(rc4InitSize), _serverEncrypt(rc4InitSize), _initialized(false)
+{
+}
+
+void PacketCrypt::DecryptRecv(uint8* data, size_t len)
+{
+ if (!_initialized)
+ return;
+
+ _clientDecrypt.UpdateData(len, data);
+}
+
+void PacketCrypt::EncryptSend(uint8* data, size_t len)
+{
+ if (!_initialized)
+ return;
+
+ _serverEncrypt.UpdateData(len, data);
+}
diff --git a/src/server/shared/Cryptography/Authentication/AuthCrypt.h b/src/server/shared/Cryptography/Authentication/PacketCrypt.h
index 8fa150068a2..36f3b81fb53 100644
--- a/src/server/shared/Cryptography/Authentication/AuthCrypt.h
+++ b/src/server/shared/Cryptography/Authentication/PacketCrypt.h
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -16,27 +15,29 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef _AUTHCRYPT_H
-#define _AUTHCRYPT_H
+#ifndef _PACKETCRYPT_H
+#define _PACKETCRYPT_H
#include "Cryptography/ARC4.h"
class BigNumber;
-class AuthCrypt
+class PacketCrypt
{
public:
- AuthCrypt();
+ PacketCrypt(uint32 rc4InitSize);
+ virtual ~PacketCrypt() { }
- void Init(BigNumber* K);
- void DecryptRecv(uint8 *, size_t);
- void EncryptSend(uint8 *, size_t);
+ virtual void Init(BigNumber* K) = 0;
+ void DecryptRecv(uint8* data, size_t length);
+ void EncryptSend(uint8* data, size_t length);
bool IsInitialized() const { return _initialized; }
- private:
+ protected:
ARC4 _clientDecrypt;
ARC4 _serverEncrypt;
bool _initialized;
};
-#endif
+
+#endif // _PACKETCRYPT_H
diff --git a/src/server/shared/Cryptography/Authentication/AuthCrypt.cpp b/src/server/shared/Cryptography/Authentication/WorldPacketCrypt.cpp
index ff94f307254..10403b84a1f 100644
--- a/src/server/shared/Cryptography/Authentication/AuthCrypt.cpp
+++ b/src/server/shared/Cryptography/Authentication/WorldPacketCrypt.cpp
@@ -16,58 +16,36 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "AuthCrypt.h"
-#include "Cryptography/HMACSHA1.h"
+#include "WorldPacketCrypt.h"
+#include "Cryptography/HmacHash.h"
#include "Cryptography/BigNumber.h"
-AuthCrypt::AuthCrypt() :
- _clientDecrypt(SHA_DIGEST_LENGTH), _serverEncrypt(SHA_DIGEST_LENGTH),
- _initialized(false)
-{ }
+WorldPacketCrypt::WorldPacketCrypt() : PacketCrypt(SHA_DIGEST_LENGTH)
+{
+}
-void AuthCrypt::Init(BigNumber* K)
+void WorldPacketCrypt::Init(BigNumber* K)
{
uint8 ServerEncryptionKey[SEED_KEY_SIZE] = { 0xCC, 0x98, 0xAE, 0x04, 0xE8, 0x97, 0xEA, 0xCA, 0x12, 0xDD, 0xC0, 0x93, 0x42, 0x91, 0x53, 0x57 };
- HmacHash serverEncryptHmac(SEED_KEY_SIZE, (uint8*)ServerEncryptionKey);
+ HmacSha1 serverEncryptHmac(SEED_KEY_SIZE, (uint8*)ServerEncryptionKey);
uint8 *encryptHash = serverEncryptHmac.ComputeHash(K);
uint8 ServerDecryptionKey[SEED_KEY_SIZE] = { 0xC2, 0xB3, 0x72, 0x3C, 0xC6, 0xAE, 0xD9, 0xB5, 0x34, 0x3C, 0x53, 0xEE, 0x2F, 0x43, 0x67, 0xCE };
- HmacHash clientDecryptHmac(SEED_KEY_SIZE, (uint8*)ServerDecryptionKey);
+ HmacSha1 clientDecryptHmac(SEED_KEY_SIZE, (uint8*)ServerDecryptionKey);
uint8 *decryptHash = clientDecryptHmac.ComputeHash(K);
- //ARC4 _serverDecrypt(encryptHash);
_clientDecrypt.Init(decryptHash);
_serverEncrypt.Init(encryptHash);
- //ARC4 _clientEncrypt(decryptHash);
// Drop first 1024 bytes, as WoW uses ARC4-drop1024.
uint8 syncBuf[1024];
memset(syncBuf, 0, 1024);
_serverEncrypt.UpdateData(1024, syncBuf);
- //_clientEncrypt.UpdateData(1024, syncBuf);
memset(syncBuf, 0, 1024);
- //_serverDecrypt.UpdateData(1024, syncBuf);
_clientDecrypt.UpdateData(1024, syncBuf);
_initialized = true;
}
-
-void AuthCrypt::DecryptRecv(uint8 *data, size_t len)
-{
- if (!_initialized)
- return;
-
- _clientDecrypt.UpdateData(len, data);
-}
-
-void AuthCrypt::EncryptSend(uint8 *data, size_t len)
-{
- if (!_initialized)
- return;
-
- _serverEncrypt.UpdateData(len, data);
-}
-
diff --git a/src/server/shared/Cryptography/Authentication/WorldPacketCrypt.h b/src/server/shared/Cryptography/Authentication/WorldPacketCrypt.h
new file mode 100644
index 00000000000..7ccca11f09d
--- /dev/null
+++ b/src/server/shared/Cryptography/Authentication/WorldPacketCrypt.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _WORLDPACKETCRYPT_H
+#define _WORLDPACKETCRYPT_H
+
+#include "PacketCrypt.h"
+
+class BigNumber;
+
+class WorldPacketCrypt : public PacketCrypt
+{
+ public:
+ WorldPacketCrypt();
+
+ void Init(BigNumber* K) override;
+};
+
+#endif // _WORLDPACKETCRYPT_H
diff --git a/src/server/shared/Cryptography/BigNumber.cpp b/src/server/shared/Cryptography/BigNumber.cpp
index 1f3fc96e28d..1c82314bdba 100644
--- a/src/server/shared/Cryptography/BigNumber.cpp
+++ b/src/server/shared/Cryptography/BigNumber.cpp
@@ -190,13 +190,19 @@ ACE_Auto_Array_Ptr<uint8> BigNumber::AsByteArray(int32 minSize, bool littleEndia
return ret;
}
-char * BigNumber::AsHexStr() const
+std::string BigNumber::AsHexStr() const
{
- return BN_bn2hex(_bn);
+ char* ch = BN_bn2hex(_bn);
+ std::string ret = ch;
+ OPENSSL_free(ch);
+ return ret;
}
-char * BigNumber::AsDecStr() const
+std::string BigNumber::AsDecStr() const
{
- return BN_bn2dec(_bn);
+ char* ch = BN_bn2dec(_bn);
+ std::string ret = ch;
+ OPENSSL_free(ch);
+ return ret;
}
diff --git a/src/server/shared/Cryptography/BigNumber.h b/src/server/shared/Cryptography/BigNumber.h
index dc553babec9..7de53b442ae 100644
--- a/src/server/shared/Cryptography/BigNumber.h
+++ b/src/server/shared/Cryptography/BigNumber.h
@@ -21,6 +21,7 @@
#include "Define.h"
#include <ace/Auto_Ptr.h>
+#include <string>
struct bignum_st;
@@ -89,8 +90,8 @@ class BigNumber
ACE_Auto_Array_Ptr<uint8> AsByteArray(int32 minSize = 0, bool littleEndian = true);
- char * AsHexStr() const;
- char * AsDecStr() const;
+ std::string AsHexStr() const;
+ std::string AsDecStr() const;
private:
struct bignum_st *_bn;
diff --git a/src/server/shared/Cryptography/HMACSHA1.cpp b/src/server/shared/Cryptography/HMACSHA1.cpp
deleted file mode 100644
index 2148a3b8a7b..00000000000
--- a/src/server/shared/Cryptography/HMACSHA1.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "HMACSHA1.h"
-#include "BigNumber.h"
-#include "Common.h"
-
-HmacHash::HmacHash(uint32 len, uint8 *seed)
-{
- HMAC_CTX_init(&m_ctx);
- HMAC_Init_ex(&m_ctx, seed, len, EVP_sha1(), NULL);
- memset(m_digest, 0, sizeof(m_digest));
-}
-
-HmacHash::~HmacHash()
-{
- HMAC_CTX_cleanup(&m_ctx);
-}
-
-void HmacHash::UpdateData(const std::string &str)
-{
- HMAC_Update(&m_ctx, (uint8 const*)str.c_str(), str.length());
-}
-
-void HmacHash::UpdateData(const uint8* data, size_t len)
-{
- HMAC_Update(&m_ctx, data, len);
-}
-
-void HmacHash::Finalize()
-{
- uint32 length = 0;
- HMAC_Final(&m_ctx, (uint8*)m_digest, &length);
- ASSERT(length == SHA_DIGEST_LENGTH);
-}
-
-uint8 *HmacHash::ComputeHash(BigNumber* bn)
-{
- HMAC_Update(&m_ctx, bn->AsByteArray().get(), bn->GetNumBytes());
- Finalize();
- return (uint8*)m_digest;
-}
diff --git a/src/server/shared/Cryptography/HmacHash.cpp b/src/server/shared/Cryptography/HmacHash.cpp
new file mode 100644
index 00000000000..2913b9fa79a
--- /dev/null
+++ b/src/server/shared/Cryptography/HmacHash.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "HmacHash.h"
+#include "BigNumber.h"
+#include "Common.h"
+
+template<HashCreateFn HashCreator, uint32 DigestLength>
+HmacHash<HashCreator, DigestLength>::HmacHash(uint32 len, uint8 *seed)
+{
+ HMAC_CTX_init(&_ctx);
+ HMAC_Init_ex(&_ctx, seed, len, HashCreator(), NULL);
+ memset(_digest, 0, DigestLength);
+}
+
+template<HashCreateFn HashCreator, uint32 DigestLength>
+HmacHash<HashCreator, DigestLength>::~HmacHash()
+{
+ HMAC_CTX_cleanup(&_ctx);
+}
+
+template<HashCreateFn HashCreator, uint32 DigestLength>
+void HmacHash<HashCreator, DigestLength>::UpdateData(const std::string &str)
+{
+ HMAC_Update(&_ctx, (uint8 const*)str.c_str(), str.length());
+}
+
+template<HashCreateFn HashCreator, uint32 DigestLength>
+void HmacHash<HashCreator, DigestLength>::UpdateData(const uint8* data, size_t len)
+{
+ HMAC_Update(&_ctx, data, len);
+}
+
+template<HashCreateFn HashCreator, uint32 DigestLength>
+void HmacHash<HashCreator, DigestLength>::Finalize()
+{
+ uint32 length = 0;
+ HMAC_Final(&_ctx, _digest, &length);
+ ASSERT(length == DigestLength);
+}
+
+template<HashCreateFn HashCreator, uint32 DigestLength>
+uint8* HmacHash<HashCreator, DigestLength>::ComputeHash(BigNumber* bn)
+{
+ HMAC_Update(&_ctx, bn->AsByteArray().get(), bn->GetNumBytes());
+ Finalize();
+ return _digest;
+}
+
+template class HmacHash<EVP_sha1, SHA_DIGEST_LENGTH>;
+template class HmacHash<EVP_sha256, SHA256_DIGEST_LENGTH>;
diff --git a/src/server/shared/Cryptography/HMACSHA1.h b/src/server/shared/Cryptography/HmacHash.h
index de1556d3c98..56ee55edda2 100644
--- a/src/server/shared/Cryptography/HMACSHA1.h
+++ b/src/server/shared/Cryptography/HmacHash.h
@@ -28,20 +28,26 @@ class BigNumber;
#define SEED_KEY_SIZE 16
+typedef EVP_MD const* (*HashCreateFn)();
+
+template<HashCreateFn HashCreator, uint32 DigestLength>
class HmacHash
{
public:
HmacHash(uint32 len, uint8 *seed);
~HmacHash();
- void UpdateData(const std::string &str);
- void UpdateData(const uint8* data, size_t len);
+ void UpdateData(std::string const& str);
+ void UpdateData(uint8 const* data, size_t len);
void Finalize();
- uint8 *ComputeHash(BigNumber* bn);
- uint8 *GetDigest() { return (uint8*)m_digest; }
- int GetLength() const { return SHA_DIGEST_LENGTH; }
+ uint8* ComputeHash(BigNumber* bn);
+ uint8* GetDigest() { return _digest; }
+ uint32 GetLength() const { return DigestLength; }
private:
- HMAC_CTX m_ctx;
- uint8 m_digest[SHA_DIGEST_LENGTH];
+ HMAC_CTX _ctx;
+ uint8 _digest[DigestLength];
};
-#endif
+typedef HmacHash<EVP_sha1, SHA_DIGEST_LENGTH> HmacSha1;
+typedef HmacHash<EVP_sha256, SHA256_DIGEST_LENGTH> HmacSha256;
+
+#endif
diff --git a/src/server/shared/Cryptography/SHA256.cpp b/src/server/shared/Cryptography/SHA256.cpp
new file mode 100644
index 00000000000..b58c7db40c6
--- /dev/null
+++ b/src/server/shared/Cryptography/SHA256.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "SHA256.h"
+#include "BigNumber.h"
+#include <stdarg.h>
+
+SHA256Hash::SHA256Hash()
+{
+ SHA256_Init(&mC);
+ memset(mDigest, 0, SHA256_DIGEST_LENGTH * sizeof(uint8));
+}
+
+SHA256Hash::~SHA256Hash()
+{
+ SHA256_Init(&mC);
+}
+
+void SHA256Hash::UpdateData(const uint8 *dta, int len)
+{
+ SHA256_Update(&mC, dta, len);
+}
+
+void SHA256Hash::UpdateData(const std::string &str)
+{
+ UpdateData((uint8 const*)str.c_str(), str.length());
+}
+
+void SHA256Hash::UpdateBigNumbers(BigNumber* bn0, ...)
+{
+ va_list v;
+ BigNumber* bn;
+
+ va_start(v, bn0);
+ bn = bn0;
+ while (bn)
+ {
+ UpdateData(bn->AsByteArray().get(), bn->GetNumBytes());
+ bn = va_arg(v, BigNumber*);
+ }
+ va_end(v);
+}
+
+void SHA256Hash::Initialize()
+{
+ SHA256_Init(&mC);
+}
+
+void SHA256Hash::Finalize(void)
+{
+ SHA256_Final(mDigest, &mC);
+}
diff --git a/src/server/shared/Cryptography/SHA256.h b/src/server/shared/Cryptography/SHA256.h
new file mode 100644
index 00000000000..78b3666dca8
--- /dev/null
+++ b/src/server/shared/Cryptography/SHA256.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SHA256_h__
+#define SHA256_h__
+
+#include "Define.h"
+#include <string>
+#include <openssl/sha.h>
+
+class BigNumber;
+
+class SHA256Hash
+{
+ public:
+ SHA256Hash();
+ ~SHA256Hash();
+
+ void UpdateBigNumbers(BigNumber* bn0, ...);
+
+ void UpdateData(const uint8 *dta, int len);
+ void UpdateData(const std::string &str);
+
+ void Initialize();
+ void Finalize();
+
+ uint8 *GetDigest(void) { return mDigest; };
+ int GetLength(void) const { return SHA256_DIGEST_LENGTH; };
+
+ private:
+ SHA256_CTX mC;
+ uint8 mDigest[SHA256_DIGEST_LENGTH];
+};
+
+#endif // SHA256_h__
diff --git a/src/server/shared/Database/Implementation/LoginDatabase.cpp b/src/server/shared/Database/Implementation/LoginDatabase.cpp
index de1e5b992e6..bccbe41a6ec 100644
--- a/src/server/shared/Database/Implementation/LoginDatabase.cpp
+++ b/src/server/shared/Database/Implementation/LoginDatabase.cpp
@@ -22,7 +22,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
if (!m_reconnecting)
m_stmts.resize(MAX_LOGINDATABASE_STATEMENTS);
- PrepareStatement(LOGIN_SEL_REALMLIST, "SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild FROM realmlist WHERE flag <> 3 ORDER BY name", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_REALMLIST, "SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild, Region, Battlegroup FROM realmlist WHERE flag <> 3 ORDER BY name", CONNECTION_SYNCH);
PrepareStatement(LOGIN_DEL_EXPIRED_IP_BANS, "DELETE FROM ip_banned WHERE unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS, "UPDATE account_banned SET active = 0 WHERE active = 1 AND unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_IP_BANNED, "SELECT * FROM ip_banned WHERE ip = ?", CONNECTION_SYNCH);
@@ -37,13 +37,14 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_SESSIONKEY, "SELECT a.sessionkey, a.id, aa.gmlevel FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_UPD_VS, "UPDATE account SET v = ?, s = ? WHERE username = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_LOGONPROOF, "UPDATE account SET sessionkey = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_SEL_LOGONCHALLENGE, "SELECT a.sha_pass_hash, a.id, a.locked, a.lock_country, a.last_ip, aa.gmlevel, a.v, a.s, a.token_key FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.username = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_LOGONCHALLENGE, "SELECT a.sha_pass_hash, a.id, a.locked, a.lock_country, a.last_ip, aa.gmlevel, a.v, a.s, a.token_key, a.battlenet_account FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_LOGON_COUNTRY, "SELECT country FROM ip2nation WHERE ip < ? ORDER BY ip DESC LIMIT 0,1", CONNECTION_SYNCH);
PrepareStatement(LOGIN_UPD_FAILEDLOGINS, "UPDATE account SET failed_logins = failed_logins + 1 WHERE username = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_FAILEDLOGINS, "SELECT id, failed_logins FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME, "SELECT id FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME, "SELECT id, username FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_BNET, "SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE battlenet_account = ? AND battlenet_index = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL, "SELECT id, username FROM account WHERE email = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_NUM_CHARS_ON_REALM, "SELECT numchars FROM realmcharacters WHERE realmid = ? AND acctid= ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_BY_IP, "SELECT id, username FROM account WHERE last_ip = ?", CONNECTION_SYNCH);
@@ -101,4 +102,20 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS, "SELECT permissionId, granted FROM rbac_account_permissions WHERE accountId = ? AND (realmId = ? OR realmId = -1) ORDER BY permissionId, realmId", CONNECTION_SYNCH);
PrepareStatement(LOGIN_INS_RBAC_ACCOUNT_PERMISSION, "INSERT INTO rbac_account_permissions (accountId, permissionId, granted, realmId) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE granted = VALUES(granted)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_DEL_RBAC_ACCOUNT_PERMISSION, "DELETE FROM rbac_account_permissions WHERE accountId = ? AND permissionId = ? AND (realmId = ? OR realmId = -1)", CONNECTION_ASYNC);
+
+ PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_INFO, "SELECT sha_pass_hash, id, locked, lock_country, last_ip, v, s FROM battlenet_accounts WHERE email = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_DEL_BNET_EXPIRED_BANS, "UPDATE battlenet_account_bans SET active = 0 WHERE active = 1 AND unbandate <> bandate AND unbandate <= UNIX_TIMESTAMP()", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_BNET_ACTIVE_ACCOUNT_BAN, "SELECT bandate, unbandate FROM battlenet_account_bans WHERE id = ? AND active = 1", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_UPD_BNET_VS_FIELDS, "UPDATE battlenet_accounts SET v = ?, s = ? WHERE email = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_BNET_SESSION_KEY, "UPDATE battlenet_accounts SET sessionKey = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_SEL_BNET_RECONNECT_INFO, "SELECT ba.id, ba.sessionKey, a.id FROM battlenet_accounts ba LEFT JOIN account a ON ba.id = a.battlenet_account WHERE ba.email = ? AND a.battlenet_index = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_BNET_GAME_ACCOUNTS, "SELECT a.battlenet_index, a.id, ab.bandate, ab.unbandate, ab.active FROM account a LEFT JOIN account_banned ab ON a.id = ab.id WHERE battlenet_account = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_BNET_GAME_ACCOUNT, "SELECT a.id, ab.bandate, ab.unbandate, ab.active FROM account a LEFT JOIN account_banned ab ON a.id = ab.id WHERE battlenet_index = ? AND battlenet_account = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_UPD_BNET_LAST_LOGIN_INFO, "UPDATE battlenet_accounts SET last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS, "SELECT rc.numchars, r.id, r.Region, r.Battlegroup, r.gamebuild FROM realmcharacters rc INNER JOIN realmlist r ON rc.realmid = r.id WHERE rc.acctid = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_INS_BNET_ACCOUNT, "INSERT INTO battlenet_accounts (`email`,`sha_pass_hash`) VALUES (?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_EMAIL_BY_ID, "SELECT email FROM battlenet_accounts WHERE id = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_ID_BY_EMAIL, "SELECT id FROM battlenet_accounts WHERE email = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_UPD_BNET_PASSWORD, "UPDATE account SET v = '', s = '', username = ?, sha_pass_hash = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_SEL_BNET_CHECK_PASSWORD, "SELECT 1 FROM battlenet_accounts WHERE id = ? AND sha_pass_hash = ?", CONNECTION_ASYNC);
}
diff --git a/src/server/shared/Database/Implementation/LoginDatabase.h b/src/server/shared/Database/Implementation/LoginDatabase.h
index 01f9fd973b6..64264984e29 100644
--- a/src/server/shared/Database/Implementation/LoginDatabase.h
+++ b/src/server/shared/Database/Implementation/LoginDatabase.h
@@ -62,6 +62,7 @@ enum LoginDatabaseStatements
LOGIN_SEL_ACCOUNT_ID_BY_NAME,
LOGIN_SEL_ACCOUNT_LIST_BY_NAME,
LOGIN_SEL_ACCOUNT_INFO_BY_NAME,
+ LOGIN_SEL_ACCOUNT_INFO_BY_BNET,
LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL,
LOGIN_SEL_NUM_CHARS_ON_REALM,
LOGIN_SEL_ACCOUNT_BY_IP,
@@ -120,6 +121,23 @@ enum LoginDatabaseStatements
LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS,
LOGIN_INS_RBAC_ACCOUNT_PERMISSION,
LOGIN_DEL_RBAC_ACCOUNT_PERMISSION,
+
+ LOGIN_SEL_BNET_ACCOUNT_INFO,
+ LOGIN_DEL_BNET_EXPIRED_BANS,
+ LOGIN_SEL_BNET_ACTIVE_ACCOUNT_BAN,
+ LOGIN_UPD_BNET_VS_FIELDS,
+ LOGIN_UPD_BNET_SESSION_KEY,
+ LOGIN_SEL_BNET_RECONNECT_INFO,
+ LOGIN_SEL_BNET_GAME_ACCOUNTS,
+ LOGIN_SEL_BNET_GAME_ACCOUNT,
+ LOGIN_UPD_BNET_LAST_LOGIN_INFO,
+ LOGIN_SEL_BNET_CHARACTER_COUNTS,
+ LOGIN_INS_BNET_ACCOUNT,
+ LOGIN_SEL_BNET_ACCOUNT_EMAIL_BY_ID,
+ LOGIN_SEL_BNET_ACCOUNT_ID_BY_EMAIL,
+ LOGIN_UPD_BNET_PASSWORD,
+ LOGIN_SEL_BNET_CHECK_PASSWORD,
+
MAX_LOGINDATABASE_STATEMENTS
};
diff --git a/src/server/shared/Utilities/Util.cpp b/src/server/shared/Utilities/Util.cpp
index f2a6f1b7622..c26b981dab9 100644
--- a/src/server/shared/Utilities/Util.cpp
+++ b/src/server/shared/Utilities/Util.cpp
@@ -524,6 +524,17 @@ void vutf8printf(FILE* out, const char *str, va_list* ap)
#endif
}
+bool Utf8ToUpperOnlyLatin(std::string& utf8String)
+{
+ std::wstring wstr;
+ if (!Utf8toWStr(utf8String, wstr))
+ return false;
+
+ std::transform(wstr.begin(), wstr.end(), wstr.begin(), wcharToUpperOnlyLatin);
+
+ return WStrToUtf8(wstr, utf8String);
+}
+
std::string ByteArrayToHexStr(uint8 const* bytes, uint32 arrayLen, bool reverse /* = false */)
{
int32 init = 0;
@@ -547,3 +558,28 @@ std::string ByteArrayToHexStr(uint8 const* bytes, uint32 arrayLen, bool reverse
return ss.str();
}
+
+void HexStrToByteArray(std::string const& str, uint8* out, bool reverse /*= false*/)
+{
+ // string must have even number of characters
+ if (str.length() & 1)
+ return;
+
+ int32 init = 0;
+ int32 end = str.length();
+ int8 op = 1;
+
+ if (reverse)
+ {
+ init = str.length() - 2;
+ end = -2;
+ op = -1;
+ }
+
+ uint32 j = 0;
+ for (int32 i = init; i != end; i += 2 * op)
+ {
+ char buffer[3] = { str[i], str[i + 1], '\0' };
+ out[j++] = strtoul(buffer, NULL, 16);
+ }
+}
diff --git a/src/server/shared/Utilities/Util.h b/src/server/shared/Utilities/Util.h
index 4d7a37d77cf..bf70bdf406a 100644
--- a/src/server/shared/Utilities/Util.h
+++ b/src/server/shared/Utilities/Util.h
@@ -344,6 +344,7 @@ bool consoleToUtf8(const std::string& conStr, std::string& utf8str);
bool Utf8FitTo(const std::string& str, std::wstring search);
void utf8printf(FILE* out, const char *str, ...);
void vutf8printf(FILE* out, const char *str, va_list* ap);
+bool Utf8ToUpperOnlyLatin(std::string& utf8String);
bool IsIPAddress(char const* ipaddress);
@@ -356,6 +357,7 @@ std::string GetAddressString(ACE_INET_Addr const& addr);
uint32 CreatePIDFile(const std::string& filename);
std::string ByteArrayToHexStr(uint8 const* bytes, uint32 length, bool reverse = false);
+void HexStrToByteArray(std::string const& str, uint8* out, bool reverse = false);
#endif
//handler for operations on large flags
diff --git a/src/server/worldserver/RemoteAccess/RASocket.cpp b/src/server/worldserver/RemoteAccess/RASocket.cpp
index 6e621ddfffe..f2c7a8e0f02 100644
--- a/src/server/worldserver/RemoteAccess/RASocket.cpp
+++ b/src/server/worldserver/RemoteAccess/RASocket.cpp
@@ -184,7 +184,7 @@ int RASocket::check_access_level(const std::string& user)
{
std::string safeUser = user;
- AccountMgr::normalizeString(safeUser);
+ Utf8ToUpperOnlyLatin(safeUser);
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ACCESS);
stmt->setString(0, safeUser);
@@ -215,10 +215,10 @@ int RASocket::check_access_level(const std::string& user)
int RASocket::check_password(const std::string& user, const std::string& pass)
{
std::string safe_user = user;
- AccountMgr::normalizeString(safe_user);
+ Utf8ToUpperOnlyLatin(safe_user);
std::string safe_pass = pass;
- AccountMgr::normalizeString(safe_pass);
+ Utf8ToUpperOnlyLatin(safe_pass);
std::string hash = AccountMgr::CalculateShaPassHash(safe_user, safe_pass);