aboutsummaryrefslogtreecommitdiff
path: root/src/server/authserver
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/authserver')
-rw-r--r--src/server/authserver/Realms/RealmList.cpp22
-rw-r--r--src/server/authserver/Realms/RealmList.h10
-rw-r--r--src/server/authserver/Server/AuthSocket.cpp2
-rw-r--r--src/server/authserver/Server/BattlenetBitStream.h26
-rw-r--r--src/server/authserver/Server/BattlenetManager.h8
-rw-r--r--src/server/authserver/Server/BattlenetPackets.cpp165
-rw-r--r--src/server/authserver/Server/BattlenetPackets.h139
-rw-r--r--src/server/authserver/Server/BattlenetSocket.cpp762
-rw-r--r--src/server/authserver/Server/BattlenetSocket.h56
9 files changed, 1100 insertions, 90 deletions
diff --git a/src/server/authserver/Realms/RealmList.cpp b/src/server/authserver/Realms/RealmList.cpp
index 4aeecfc0aaa..bd856623faf 100644
--- a/src/server/authserver/Realms/RealmList.cpp
+++ b/src/server/authserver/Realms/RealmList.cpp
@@ -18,6 +18,7 @@
#include "Common.h"
#include "RealmList.h"
+#include "BattlenetManager.h"
#include "Database/DatabaseEnv.h"
RealmList::RealmList() : m_UpdateInterval(0), m_NextUpdateTime(time(NULL)) { }
@@ -31,7 +32,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 +50,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 +94,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 +109,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..ab453720827 100644
--- a/src/server/authserver/Realms/RealmList.h
+++ b/src/server/authserver/Realms/RealmList.h
@@ -51,8 +51,15 @@ struct Realm
AccountTypes allowedSecurityLevel;
float populationLevel;
uint32 gamebuild;
+ uint8 Region;
+ uint8 Battlegroup;
};
+namespace Battlenet
+{
+ struct RealmId;
+}
+
/// Storage object for the list of realms on the server
class RealmList
{
@@ -71,10 +78,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..3afce0d77d9 100644
--- a/src/server/authserver/Server/AuthSocket.cpp
+++ b/src/server/authserver/Server/AuthSocket.cpp
@@ -986,7 +986,7 @@ bool AuthSocket::_HandleRealmList()
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/BattlenetBitStream.h b/src/server/authserver/Server/BattlenetBitStream.h
index bff1bffb3ab..a0bff314e8f 100644
--- a/src/server/authserver/Server/BattlenetBitStream.h
+++ b/src/server/authserver/Server/BattlenetBitStream.h
@@ -22,6 +22,8 @@
#include <exception>
#include <string>
#include <vector>
+#include <type_traits>
+#include <ace/Auto_Ptr.h>
#include <ace/Stack_Trace.h>
namespace Battlenet
@@ -70,14 +72,14 @@ namespace Battlenet
return str;
}
- uint8* ReadBytes(uint32 count)
+ ACE_Auto_Array_Ptr<uint8> ReadBytes(uint32 count)
{
AlignToNextByte();
if (_readPos + count * 8 > _numBits)
throw BitStreamPositionException();
- uint8* buf = new uint8[count];
- memcpy(buf, &_buffer[_readPos >> 3], count);
+ ACE_Auto_Array_Ptr<uint8> buf(new uint8[count]);
+ memcpy(buf.get(), &_buffer[_readPos >> 3], count);
_readPos += count * 8;
return buf;
}
@@ -105,7 +107,9 @@ namespace Battlenet
template<typename T>
T Read(uint32 bitCount)
{
- T ret = 0;
+ 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);
@@ -114,10 +118,11 @@ namespace Battlenet
bitsLeftInByte = bitCount;
bitCount -= bitsLeftInByte;
- ret |= static_cast<T>((uint64)(_buffer[_readPos >> 3] >> bitPos & (uint32)((uint8)(1 << bitsLeftInByte) - 1)) << bitCount);
+ ret |= (uint64)(_buffer[_readPos >> 3] >> bitPos & (uint32)((uint8)(1 << bitsLeftInByte) - 1)) << bitCount;
_readPos += bitsLeftInByte;
}
- return ret;
+
+ return static_cast<T>(ret);
}
void WriteString(std::string const& str, uint32 bitCount, int32 baseLength = 0)
@@ -141,7 +146,12 @@ namespace Battlenet
_writePos += count * 8;
}
- //WriteFloat
+ 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();
@@ -157,6 +167,8 @@ namespace Battlenet
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();
diff --git a/src/server/authserver/Server/BattlenetManager.h b/src/server/authserver/Server/BattlenetManager.h
index 08c98645ebd..7bc12f94c62 100644
--- a/src/server/authserver/Server/BattlenetManager.h
+++ b/src/server/authserver/Server/BattlenetManager.h
@@ -73,6 +73,14 @@ namespace Battlenet
uint32 DataSize;
uint8* Data;
};
+
+ struct RealmId
+ {
+ uint8 Region;
+ uint8 Battlegroup;
+ uint32 Index;
+ uint32 Build;
+ };
}
class BattlenetMgr
diff --git a/src/server/authserver/Server/BattlenetPackets.cpp b/src/server/authserver/Server/BattlenetPackets.cpp
index bcb4f4e4898..6fca44234f9 100644
--- a/src/server/authserver/Server/BattlenetPackets.cpp
+++ b/src/server/authserver/Server/BattlenetPackets.cpp
@@ -75,6 +75,12 @@ std::string Battlenet::AuthChallenge::ToString() const
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);
@@ -93,7 +99,7 @@ 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) << ", BlobSize " << module->DataSize;
+ 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();
}
@@ -101,7 +107,7 @@ std::string Battlenet::ProofRequest::ToString() const
Battlenet::ProofResponse::~ProofResponse()
{
for (size_t i = 0; i < Modules.size(); ++i)
- delete[] Modules[i].Data;
+ delete Modules[i];
}
void Battlenet::ProofResponse::Read()
@@ -109,9 +115,9 @@ void Battlenet::ProofResponse::Read()
Modules.resize(_stream.Read<uint32>(3));
for (size_t i = 0; i < Modules.size(); ++i)
{
- ModuleData& data = Modules[i];
- data.Size = _stream.Read<uint32>(10);
- data.Data = _stream.ReadBytes(data.Size);
+ BitStream*& dataStream = Modules[i];
+ dataStream = new BitStream(_stream.Read<uint32>(10));
+ memcpy(dataStream->GetBuffer(), _stream.ReadBytes(dataStream->GetSize()).get(), dataStream->GetSize());
}
}
@@ -119,10 +125,10 @@ std::string Battlenet::ProofResponse::ToString() const
{
std::ostringstream stream;
stream << "Battlenet::ProofResponse Modules " << Modules.size();
- for (ModuleData const& module : Modules)
+ for (BitStream* module : Modules)
{
- std::string hexStr = ByteArrayToHexStr(module.Data, module.Size);
- stream << std::endl << "Battlenet::ProofResponse::ModuleData Size: " << module.Size << ", Data: " << hexStr;
+ std::string hexStr = ByteArrayToHexStr(module->GetBuffer(), module->GetSize());
+ stream << std::endl << "Battlenet::ProofResponse::ModuleData Size: " << module->GetSize() << ", Data: " << hexStr;
}
return stream.str();
@@ -156,7 +162,20 @@ void Battlenet::AuthComplete::Write()
}
}
- // todo more
+ _stream.WriteString(FirstName, 8); // First name
+ _stream.WriteString(LastName, 8); // Last name - not set for WoW
+
+ _stream.Write(GameAccountId, 32);
+
+ _stream.Write(2, 8);
+ _stream.Write(0, 64);
+ _stream.Write(2, 8);
+
+ _stream.WriteString(GameAccountName, 5, -1);
+
+ _stream.Write(0, 64); // Account flags
+ _stream.Write(0, 32);
+ _stream.Write(0, 1);
}
else
{
@@ -174,8 +193,8 @@ void Battlenet::AuthComplete::Write()
_stream.Write(ErrorType, 2);
if (ErrorType == 1)
{
- _stream.Write<uint16>(Result, 16);
- _stream.Write(0, 32);
+ _stream.Write(Result, 16);
+ _stream.Write(0x80000000, 32);
}
}
}
@@ -190,3 +209,127 @@ void Battlenet::AuthComplete::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.Region, 8);
+ _stream.Write(0, 12);
+ _stream.Write(entry.Realm.Battlegroup, 8);
+ _stream.Write(entry.Realm.Index, 32);
+ _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 " << entry.Realm.Region << " Battlegroup " << entry.Realm.Region << " Index " << entry.Realm.Index << " Characters " << entry.CharacterCount;
+
+ return stream.str().c_str();
+}
+
+void Battlenet::RealmUpdate::Write()
+{
+ _stream.Write(true, 1); // Success
+ _stream.Write(Timezone, 32);
+ _stream.WriteFloat(Population);
+ _stream.Write(Lock, 8);
+ _stream.Write(0, 19);
+ _stream.Write(Type + std::numeric_limits<int32>::min(), 32);
+ _stream.WriteString(Name, 10);
+
+ _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.Write(Flags, 8);
+ _stream.Write(Region, 8);
+ _stream.Write(0, 12);
+ _stream.Write(Battlegroup, 8);
+ _stream.Write(Index, 32);
+}
+
+std::string Battlenet::RealmUpdate::ToString() const
+{
+ std::ostringstream stream;
+ stream << "Battlenet::RealmUpdate Timezone " << Timezone << " Population " << Population << " Lock " << Lock << " Type " << Type << " Name " << Name
+ << " Flags " << Flags << " Region " << Region << " Battlegroup " << Battlegroup << " Index " << Index;
+
+ if (!Version.empty())
+ stream << " Version " << Version;
+
+ return stream.str().c_str();
+}
+
+void Battlenet::RealmJoinRequest::Read()
+{
+ ClientSeed = _stream.Read<uint32>(32);
+ Unknown = _stream.Read<uint32>(20);
+ Realm.Region = _stream.Read<uint8>(8);
+ _stream.Read<uint16>(12);
+ Realm.Battlegroup = _stream.Read<uint8>(8);
+ Realm.Index = _stream.Read<uint32>(32);
+}
+
+std::string Battlenet::RealmJoinRequest::ToString() const
+{
+ std::ostringstream stream;
+ stream << "Battlenet::RealmJoinRequest ClientSeed" << ClientSeed << " Region " << Realm.Region << " Battlegroup " << Realm.Battlegroup << " Index " << Realm.Index;
+ return stream.str().c_str();
+}
+
+void Battlenet::RealmJoinResult::Write()
+{
+ _stream.Write(0, 1); // Fail
+ _stream.Write(ServerSeed, 32);
+ _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);
+ }
+
+ _stream.Write(0, 5); // IPv6 addresses
+}
+
+std::string Battlenet::RealmJoinResult::ToString() const
+{
+ return "Battlenet::RealmJoinResult";
+}
diff --git a/src/server/authserver/Server/BattlenetPackets.h b/src/server/authserver/Server/BattlenetPackets.h
index 3b02c0c602a..bea9b5649e5 100644
--- a/src/server/authserver/Server/BattlenetPackets.h
+++ b/src/server/authserver/Server/BattlenetPackets.h
@@ -31,8 +31,6 @@ namespace Battlenet
enum Channel
{
- NOT_SPECIFIED = -1,
-
AUTHENTICATION = 0,
CREEP = 1,
WOW = 2
@@ -48,10 +46,29 @@ namespace Battlenet
SMSG_AUTH_PROOF_REQUEST = 0x2,
};
+ enum CreepOpcodes
+ {
+ CMSG_PING = 0x0,
+ CMSG_ENABLE_ENCRYPTION = 0x5,
+
+ SMSG_PONG = 0x0
+ };
+
+ enum WoWOpcodes
+ {
+ CMSG_REALM_UPDATE = 0x0,
+ 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(NOT_SPECIFIED) { }
+ PacketHeader() : Opcode(0), Channel(AUTHENTICATION) { }
uint32 Opcode;
int32 Channel;
@@ -142,11 +159,12 @@ namespace Battlenet
{
public:
ProofRequest() : ServerPacket(PacketHeader(SMSG_AUTH_PROOF_REQUEST, AUTHENTICATION)) { }
+ ~ProofRequest();
void Write() override;
std::string ToString() const override;
- std::vector<ModuleInfo const*> Modules;
+ std::vector<ModuleInfo*> Modules;
};
class ProofResponse final : public ClientPacket
@@ -159,23 +177,18 @@ namespace Battlenet
~ProofResponse();
- struct ModuleData
- {
- uint32 Size;
- uint8* Data;
- };
-
void Read() override;
std::string ToString() const override;
- std::vector<ModuleData> Modules;
+ 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(1000000), Rate(1000)
+ Result(AUTH_OK), ErrorType(0), PingTimeout(120000), Threshold(25000000), Rate(1000),
+ FirstName(""), LastName(""), GameAccountId(0), GameAccountName("")
{
}
@@ -192,6 +205,108 @@ namespace Battlenet
uint32 Rate;
std::string FirstName;
std::string LastName;
+ uint32 GameAccountId;
+ std::string GameAccountName;
+ uint64 AccountFlags;
+ };
+
+ 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;
};
}
diff --git a/src/server/authserver/Server/BattlenetSocket.cpp b/src/server/authserver/Server/BattlenetSocket.cpp
index edd73080270..65e12aa602d 100644
--- a/src/server/authserver/Server/BattlenetSocket.cpp
+++ b/src/server/authserver/Server/BattlenetSocket.cpp
@@ -18,15 +18,134 @@
#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_CHALLENGE_NEW, Battlenet::AUTHENTICATION)] = &Battlenet::Socket::HandleAuthChallenge;
+ 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_REALM_UPDATE, Battlenet::WOW)] = &Battlenet::Socket::HandleRealmUpdate;
+ 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::Socket(RealmSocket& socket) : _socket(socket), _accountId(0), _accountName(), _locale(),
+ _os(), _build(0), _gameAccountId(0), _accountSecurityLevel(SEC_PLAYER)
+{
+ 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);
+
+ char* v_hex = v.AsHexStr();
+ char* s_hex = s.AsHexStr();
+
+ LoginDatabase.PExecute("UPDATE battlenet_accounts SET s = '%s', v = '%s' WHERE email ='%s'", s_hex, v_hex, _accountName.c_str());
+
+ OPENSSL_free(v_hex);
+ OPENSSL_free(s_hex);
+}
+
+ACE_INET_Addr const& Battlenet::Socket::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;
+}
+
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();
- printf("%s\n", info.ToString().c_str());
-
if (info.Program != "WoW")
{
AuthComplete complete;
@@ -66,109 +185,642 @@ bool Battlenet::Socket::HandleAuthChallenge(PacketHeader& header, BitStream& pac
Send(complete);
return true;
}
+
+ if (component.Platform == "base")
+ _build = component.Build;
}
_accountName = info.Login;
_locale = info.Locale;
_os = info.Platform;
+ Utf8ToUpperOnlyLatin(_accountName);
+ LoginDatabase.EscapeString(_accountName);
+ // 0 1 2 3 4 5 6
+ QueryResult result = LoginDatabase.PQuery("SELECT sha_pass_hash, id, locked, lock_country, last_ip, v, s FROM battlenet_accounts WHERE email = '%s'", _accountName.c_str());
+ 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("UPDATE battlenet_account_bans SET active = 0 WHERE active = 1 AND unbandate <> bandate AND unbandate <= UNIX_TIMESTAMP()");
+
+ // If the account is banned, reject the logon attempt
+ QueryResult banresult = LoginDatabase.PQuery("SELECT bandate, unbandate FROM battlenet_account_bans WHERE id = %u AND active = 1", _accountId);
+ 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 = new ModuleInfo(*sBattlenetMgr->GetModule({ _os, "Password" }));
+ ModuleInfo* thumbprint = new ModuleInfo(*sBattlenetMgr->GetModule({ _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;
+
+ 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(b.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::HandleAuthProofResponse(PacketHeader& header, BitStream& packet)
{
- ProofResponse response(header, packet);
- response.Read();
+ ProofResponse proof(header, packet);
+ proof.Read();
- printf("%s\n", response.ToString().c_str());
+ if (_modulesWaitingForData.size() < proof.Modules.size())
+ {
+ AuthComplete complete;
+ complete.SetAuthResult(AUTH_CORRUPTED_MODULE);
+ Send(complete);
+ return true;
+ }
- AuthComplete complete;
- complete.SetAuthResult(AUTH_USE_GRUNT_LOGON);
- Send(complete);
+ 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;
}
-std::map<Battlenet::PacketHeader, Battlenet::Socket::PacketHandler> InitHandlers()
+bool Battlenet::Socket::HandlePing(PacketHeader& /*header*/, BitStream& /*packet*/)
{
- 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_CHALLENGE_NEW, Battlenet::AUTHENTICATION)] = &Battlenet::Socket::HandleAuthChallenge;
- handlers[Battlenet::PacketHeader(Battlenet::CMSG_AUTH_PROOF_RESPONSE, Battlenet::AUTHENTICATION)] = &Battlenet::Socket::HandleAuthProofResponse;
+ Pong pong;
+ Send(pong);
+ return true;
+}
- return handlers;
+bool Battlenet::Socket::HandleEnableEncryption(PacketHeader& /*header*/, BitStream& packet)
+{
+ _crypt.Init(&K);
+ _crypt.DecryptRecv(packet.GetBuffer() + 2, packet.GetSize() - 2);
+ return false;
}
-std::map<Battlenet::PacketHeader, Battlenet::Socket::PacketHandler> Handlers = InitHandlers();
+bool Battlenet::Socket::HandleRealmUpdate(PacketHeader& /*header*/, BitStream& /*packet*/)
+{
+ sRealmList->UpdateIfNeed();
+
+ RealmCharacterCounts counts;
+
+ ACE_INET_Addr clientAddr;
+ _socket.peer().get_remote_addr(clientAddr);
+
+ for (RealmList::RealmMap::const_iterator i = sRealmList->begin(); i != sRealmList->end(); ++i)
+ {
+ Realm const& realm = i->second;
+
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_NUM_CHARS_ON_REALM);
+ stmt->setUInt32(0, realm.m_ID);
+ stmt->setUInt32(1, _gameAccountId);
+
+ if (PreparedQueryResult result = LoginDatabase.Query(stmt))
+ counts.CharacterCounts.push_back({ { realm.Region, realm.Battlegroup, realm.m_ID, 0 }, (*result)[0].GetUInt8() });
+
+ 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->HotfixVersion;
+
+ update->Version = version.str();
+ update->Address = GetAddressForClient(realm, clientAddr);
+ update->Build = realm.gamebuild;
+ }
-Battlenet::Socket::Socket(RealmSocket& socket) : _socket(socket), _currentChannel(0)
+ 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)
+ {
+ Send(result);
+ return true;
+ }
+
+ result.ServerSeed = uint32(rand32());
+
+ uint8 sessionKey[40];
+ HmacHash hmac(K.GetNumBytes(), K.AsByteArray().get(), EVP_sha1(), SHA_DIGEST_LENGTH);
+ 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());
+
+ HmacHash hmac2(K.GetNumBytes(), K.AsByteArray().get(), EVP_sha1(), SHA_DIGEST_LENGTH);
+ 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()
{
- while (1)
- {
- size_t length = _socket.recv_len();
- if (!length)
- return;
+ size_t length = _socket.recv_len();
+ if (!length)
+ return;
- BitStream packet(length);
- if (!_socket.recv((char*)packet.GetBuffer(), length))
- return;
+ BitStream packet(length);
+ if (!_socket.recv((char*)packet.GetBuffer(), length))
+ return;
- while (!packet.IsRead())
+ _crypt.DecryptRecv(packet.GetBuffer(), length);
+
+ while (!packet.IsRead())
+ {
+ try
{
- try
- {
- PacketHeader header;
- header.Opcode = packet.Read<uint32>(6);
- if (packet.Read<uint32>(1))
- _currentChannel = header.Channel = packet.Read<int32>(4);
- else
- header.Channel = _currentChannel;
-
- printf("Battlenet::Socket::OnRead %s\n", header.ToString().c_str());
- std::map<PacketHeader, PacketHandler>::const_iterator itr = Handlers.find(header);
- if (itr != Handlers.end())
- {
- if (!(this->*(itr->second))(header, packet))
- {
- _socket.shutdown();
- return;
- }
- }
- else
- printf("Battlenet::Socket::OnRead Unhandled opcode %s\n", header.ToString().c_str());
+ PacketHeader header;
+ header.Opcode = packet.Read<uint32>(6);
+ if (packet.Read<bool>(1))
+ header.Channel = packet.Read<int32>(4);
- packet.AlignToNextByte();
+ 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;
}
- catch (BitStreamPositionException const& e)
+ else
{
- printf("Battlenet::Socket::OnRead Exception: %s\n", e.what());
- _socket.shutdown();
+ 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()
{
- printf("Battlenet::Socket::OnAccept\n");
+ TC_LOG_TRACE("server.battlenet", "Battlenet::Socket::OnAccept");
}
void Battlenet::Socket::OnClose()
{
- printf("Battlenet::Socket::OnClose\n");
+ TC_LOG_TRACE("server.battlenet", "Battlenet::Socket::OnClose");
}
void Battlenet::Socket::Send(ServerPacket& packet)
{
- printf("Battlenet::Socket::Send %s\n", 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;
+ QueryResult result = LoginDatabase.PQuery("SELECT a.username, a.id, ab.bandate, ab.unbandate, ab.active FROM account a LEFT JOIN account_banned ab ON a.id = ab.id WHERE battlenet_account = '%u'", _accountId);
+ 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());
+
+ BitStream stream;
+ ModuleInfo* password = new ModuleInfo(*sBattlenetMgr->GetModule({ _os, "Password" }));
+ uint8 state = 3;
+
+ stream.WriteBytes(&state, 1);
+ stream.WriteBytes(M.AsByteArray(32).get(), 32);
+ stream.WriteBytes(b.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();
+ accounts.Write(2, 8);
+ accounts.WriteString(fields[0].GetString(), 8);
+ } while (result->NextRow());
+
+ ModuleInfo* selectGameAccount = new ModuleInfo(*sBattlenetMgr->GetModule({ _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 true;
+ }
+
+ _gameAccountId = (*result)[1].GetUInt32();
+
+ request->Modules.push_back(new ModuleInfo(*sBattlenetMgr->GetModule({ _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);
+
+ LoginDatabase.EscapeString(account);
+ QueryResult result = LoginDatabase.PQuery("SELECT a.id, ab.bandate, ab.unbandate, ab.active FROM account a LEFT JOIN account_banned ab ON a.id = ab.id WHERE username = '%s' AND battlenet_account = '%u'", account.c_str(), _accountId);
+ 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::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 true;
+ }
+
+ _gameAccountId = fields[0].GetUInt32();
+
+ ProofRequest* request = new ProofRequest();
+ request->Modules.push_back(new ModuleInfo(*sBattlenetMgr->GetModule({ _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 << _gameAccountId << "#1";
+
+ complete->GameAccountId = _gameAccountId;
+ complete->GameAccountName = str.str();
+ complete->AccountFlags = 0x800000; // 0x1 IsGMAccount, 0x8 IsTrialAccount, 0x800000 IsProPassAccount
+
+ LoginDatabase.PExecute("UPDATE battlenet_accounts SET last_ip = '%s', last_login = NOW(), locale = %u, failed_logins = 0, os = '%s' WHERE id = %u", _socket.getRemoteAddress().c_str(), GetLocaleByName(_locale), _os.c_str(), _accountId);
+ }
+ else
+ complete->SetAuthResult(AUTH_BAD_VERSION_HASH);
+
+ ReplaceResponse(response, complete);
+ return false;
+}
+
+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
index b4db6c46251..e8014e790a8 100644
--- a/src/server/authserver/Server/BattlenetSocket.h
+++ b/src/server/authserver/Server/BattlenetSocket.h
@@ -21,22 +21,48 @@
#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_COUNT
+ };
+
class Socket : public RealmSocket::Session
{
public:
+ static uint32 const SRP6_V_Size;
+ static uint32 const SRP6_S_Size;
+
Socket(RealmSocket& socket);
typedef bool(Socket::*PacketHandler)(PacketHeader& socket, BitStream& packet);
+ // Auth
bool HandleAuthChallenge(PacketHeader& header, BitStream& packet);
bool HandleAuthProofResponse(PacketHeader& header, BitStream& packet);
+ // Creep
+ bool HandlePing(PacketHeader& header, BitStream& packet);
+ bool HandleEnableEncryption(PacketHeader& header, BitStream& packet);
+
+ // WoW
+ bool HandleRealmUpdate(PacketHeader& header, BitStream& packet);
+ bool HandleRealmJoinRequest(PacketHeader& header, BitStream& packet);
+
void OnRead() override;
void OnAccept() override;
void OnClose() override;
@@ -44,12 +70,40 @@ namespace Battlenet
void Send(ServerPacket& packet);
private:
+ void _SetVSFields(std::string const& rI);
+ static ACE_INET_Addr const& GetAddressForClient(Realm const& realm, ACE_INET_Addr const& clientAddr);
+
+ 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 UnhandledModule(BitStream* dataStream, ServerPacket** response);
+
RealmSocket& _socket;
- uint32 _currentChannel;
+ uint32 _accountId;
std::string _accountName;
std::string _locale;
std::string _os;
+ uint32 _build;
+ uint32 _gameAccountId;
+ AccountTypes _accountSecurityLevel;
+
+ BigNumber N;
+ BigNumber g;
+ BigNumber k;
+
+ BigNumber I;
+ BigNumber s;
+ BigNumber v;
+
+ BigNumber b;
+ BigNumber B;
+ BigNumber K; // session key
+
+ std::queue<ModuleType> _modulesWaitingForData;
PacketCrypt _crypt;
};