mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Fix auth protocol to match real WoW 3.3.5a client format
Three critical bugs fixed: - LOGON_CHALLENGE request: set protocol byte to 0x03 (was 0x00) and reverse FourCC strings (game/platform/os/locale) to match real client - Response parsers: remove double-read of opcode byte that shifted all field reads by one, preventing successful auth with real servers - LOGON_PROOF response sizes: success=32 bytes, failure=4 bytes to match TrinityCore/AzerothCore format Also adds missing auth result codes (0x13-0x20, 0xFF) including IGR_WITHOUT_BNET (0x17) which Warmane was returning.
This commit is contained in:
parent
1b414d24d8
commit
5ef11fdc7d
4 changed files with 53 additions and 52 deletions
|
|
@ -35,6 +35,15 @@ enum class AuthResult : uint8_t {
|
|||
LOCK_ENFORCED = 0x10,
|
||||
TRIAL_EXPIRED = 0x11,
|
||||
BATTLE_NET = 0x12,
|
||||
ANTI_INDULGENCE = 0x13,
|
||||
EXPIRED = 0x14,
|
||||
NO_GAME_ACCOUNT = 0x15,
|
||||
CHARGEBACK = 0x16,
|
||||
IGR_WITHOUT_BNET = 0x17,
|
||||
GAME_ACCOUNT_LOCKED = 0x18,
|
||||
UNLOCKABLE_LOCK = 0x19,
|
||||
CONVERSION_REQUIRED = 0x20,
|
||||
DISCONNECTED = 0xFF,
|
||||
};
|
||||
|
||||
const char* getAuthResultString(AuthResult result);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,17 @@ const char* getAuthResultString(AuthResult result) {
|
|||
case AuthResult::LOCK_ENFORCED: return "Account locked - check your email";
|
||||
case AuthResult::TRIAL_EXPIRED: return "Trial period has expired";
|
||||
case AuthResult::BATTLE_NET: return "Battle.net error";
|
||||
case AuthResult::ANTI_INDULGENCE: return "Anti-indulgence time limit reached";
|
||||
case AuthResult::EXPIRED: return "Account subscription has expired";
|
||||
case AuthResult::NO_GAME_ACCOUNT: return "No game account found - create one on the server website";
|
||||
case AuthResult::CHARGEBACK: return "Account locked due to chargeback";
|
||||
case AuthResult::IGR_WITHOUT_BNET:
|
||||
return "Internet Game Room access denied - account may require "
|
||||
"registration on the server website";
|
||||
case AuthResult::GAME_ACCOUNT_LOCKED: return "Game account is locked";
|
||||
case AuthResult::UNLOCKABLE_LOCK: return "Account is locked and cannot be unlocked";
|
||||
case AuthResult::CONVERSION_REQUIRED: return "Account conversion required";
|
||||
case AuthResult::DISCONNECTED: return "Disconnected from server";
|
||||
default:
|
||||
snprintf(authResultBuf, sizeof(authResultBuf),
|
||||
"Server rejected login (error code 0x%02X)",
|
||||
|
|
|
|||
|
|
@ -12,25 +12,33 @@ network::Packet LogonChallengePacket::build(const std::string& account, const Cl
|
|||
std::string upperAccount = account;
|
||||
std::transform(upperAccount.begin(), upperAccount.end(), upperAccount.begin(), ::toupper);
|
||||
|
||||
// Calculate packet size
|
||||
// Opcode(1) + unknown(1) + size(2) + game(4) + version(3) + build(2) +
|
||||
// platform(4) + os(4) + locale(4) + timezone(4) + ip(4) + accountLen(1) + account(N)
|
||||
// Calculate payload size (everything after cmd + error + size)
|
||||
// game(4) + version(3) + build(2) + platform(4) + os(4) + locale(4) +
|
||||
// timezone(4) + ip(4) + accountLen(1) + account(N)
|
||||
uint16_t payloadSize = 30 + upperAccount.length();
|
||||
|
||||
network::Packet packet(static_cast<uint16_t>(AuthOpcode::LOGON_CHALLENGE));
|
||||
|
||||
// Unknown byte
|
||||
packet.writeUInt8(0x00);
|
||||
// Protocol version (real WoW 3.3.5a client sends 3)
|
||||
packet.writeUInt8(0x03);
|
||||
|
||||
// Payload size
|
||||
packet.writeUInt16(payloadSize);
|
||||
|
||||
// Game name (4 bytes, null-padded)
|
||||
packet.writeBytes(reinterpret_cast<const uint8_t*>(info.game.c_str()),
|
||||
std::min<size_t>(4, info.game.length()));
|
||||
for (size_t i = info.game.length(); i < 4; ++i) {
|
||||
packet.writeUInt8(0);
|
||||
}
|
||||
// Helper: write a FourCC string as reversed bytes (little-endian FourCC)
|
||||
// Real WoW client stores FourCC as big-endian uint32, written in LE byte order
|
||||
auto writeFourCC = [&packet](const std::string& str) {
|
||||
uint8_t buf[4] = {0, 0, 0, 0};
|
||||
for (size_t i = 0; i < std::min<size_t>(4, str.length()); ++i) {
|
||||
buf[i] = static_cast<uint8_t>(str[i]);
|
||||
}
|
||||
for (int i = 3; i >= 0; --i) {
|
||||
packet.writeUInt8(buf[i]);
|
||||
}
|
||||
};
|
||||
|
||||
// Game name (4 bytes, reversed FourCC)
|
||||
writeFourCC(info.game);
|
||||
|
||||
// Version (3 bytes)
|
||||
packet.writeUInt8(info.majorVersion);
|
||||
|
|
@ -40,26 +48,14 @@ network::Packet LogonChallengePacket::build(const std::string& account, const Cl
|
|||
// Build (2 bytes)
|
||||
packet.writeUInt16(info.build);
|
||||
|
||||
// Platform (4 bytes, null-padded)
|
||||
packet.writeBytes(reinterpret_cast<const uint8_t*>(info.platform.c_str()),
|
||||
std::min<size_t>(4, info.platform.length()));
|
||||
for (size_t i = info.platform.length(); i < 4; ++i) {
|
||||
packet.writeUInt8(0);
|
||||
}
|
||||
// Platform (4 bytes, reversed FourCC)
|
||||
writeFourCC(info.platform);
|
||||
|
||||
// OS (4 bytes, null-padded)
|
||||
packet.writeBytes(reinterpret_cast<const uint8_t*>(info.os.c_str()),
|
||||
std::min<size_t>(4, info.os.length()));
|
||||
for (size_t i = info.os.length(); i < 4; ++i) {
|
||||
packet.writeUInt8(0);
|
||||
}
|
||||
// OS (4 bytes, reversed FourCC)
|
||||
writeFourCC(info.os);
|
||||
|
||||
// Locale (4 bytes, null-padded)
|
||||
packet.writeBytes(reinterpret_cast<const uint8_t*>(info.locale.c_str()),
|
||||
std::min<size_t>(4, info.locale.length()));
|
||||
for (size_t i = info.locale.length(); i < 4; ++i) {
|
||||
packet.writeUInt8(0);
|
||||
}
|
||||
// Locale (4 bytes, reversed FourCC)
|
||||
writeFourCC(info.locale);
|
||||
|
||||
// Timezone
|
||||
packet.writeUInt32(info.timezone);
|
||||
|
|
@ -80,14 +76,9 @@ network::Packet LogonChallengePacket::build(const std::string& account, const Cl
|
|||
}
|
||||
|
||||
bool LogonChallengeResponseParser::parse(network::Packet& packet, LogonChallengeResponse& response) {
|
||||
// Read opcode (should be LOGON_CHALLENGE)
|
||||
uint8_t opcode = packet.readUInt8();
|
||||
if (opcode != static_cast<uint8_t>(AuthOpcode::LOGON_CHALLENGE)) {
|
||||
LOG_ERROR("Invalid opcode in LOGON_CHALLENGE response: ", (int)opcode);
|
||||
return false;
|
||||
}
|
||||
// Note: opcode byte already consumed by handlePacket()
|
||||
|
||||
// Unknown byte
|
||||
// Unknown/protocol byte
|
||||
packet.readUInt8();
|
||||
|
||||
// Status
|
||||
|
|
@ -180,12 +171,7 @@ network::Packet LogonProofPacket::build(const std::vector<uint8_t>& A,
|
|||
}
|
||||
|
||||
bool LogonProofResponseParser::parse(network::Packet& packet, LogonProofResponse& response) {
|
||||
// Read opcode (should be LOGON_PROOF)
|
||||
uint8_t opcode = packet.readUInt8();
|
||||
if (opcode != static_cast<uint8_t>(AuthOpcode::LOGON_PROOF)) {
|
||||
LOG_ERROR("Invalid opcode in LOGON_PROOF response: ", (int)opcode);
|
||||
return false;
|
||||
}
|
||||
// Note: opcode byte already consumed by handlePacket()
|
||||
|
||||
// Status
|
||||
response.status = packet.readUInt8();
|
||||
|
|
@ -222,12 +208,7 @@ network::Packet RealmListPacket::build() {
|
|||
}
|
||||
|
||||
bool RealmListResponseParser::parse(network::Packet& packet, RealmListResponse& response) {
|
||||
// Read opcode (should be REALM_LIST)
|
||||
uint8_t opcode = packet.readUInt8();
|
||||
if (opcode != static_cast<uint8_t>(AuthOpcode::REALM_LIST)) {
|
||||
LOG_ERROR("Invalid opcode in REALM_LIST response: ", (int)opcode);
|
||||
return false;
|
||||
}
|
||||
// Note: opcode byte already consumed by handlePacket()
|
||||
|
||||
// Packet size (2 bytes) - we already know the size, skip it
|
||||
uint16_t packetSize = packet.readUInt16();
|
||||
|
|
|
|||
|
|
@ -224,14 +224,14 @@ size_t TCPSocket::getExpectedPacketSize(uint8_t opcode) {
|
|||
return 0; // Need more data to determine
|
||||
|
||||
case 0x01: // LOGON_PROOF response
|
||||
// opcode(1) + status(1) + M2(20) = 22 bytes on success
|
||||
// opcode(1) + status(1) = 2 bytes on failure
|
||||
// Success: opcode(1) + status(1) + M2(20) + accountFlags(4) + surveyId(4) + loginFlags(2) = 32
|
||||
// Failure: opcode(1) + status(1) + padding(2) = 4
|
||||
if (receiveBuffer.size() >= 2) {
|
||||
uint8_t status = receiveBuffer[1];
|
||||
if (status == 0x00) {
|
||||
return 22; // Success
|
||||
return 32; // Success
|
||||
} else {
|
||||
return 2; // Failure
|
||||
return 4; // Failure
|
||||
}
|
||||
}
|
||||
return 0; // Need more data
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue