mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
290 lines
7.8 KiB
C++
290 lines
7.8 KiB
C++
|
|
#include "auth/auth_handler.hpp"
|
||
|
|
#include "network/tcp_socket.hpp"
|
||
|
|
#include "network/packet.hpp"
|
||
|
|
#include "core/logger.hpp"
|
||
|
|
|
||
|
|
namespace wowee {
|
||
|
|
namespace auth {
|
||
|
|
|
||
|
|
AuthHandler::AuthHandler() {
|
||
|
|
LOG_DEBUG("AuthHandler created");
|
||
|
|
}
|
||
|
|
|
||
|
|
AuthHandler::~AuthHandler() {
|
||
|
|
disconnect();
|
||
|
|
}
|
||
|
|
|
||
|
|
bool AuthHandler::connect(const std::string& host, uint16_t port) {
|
||
|
|
LOG_INFO("Connecting to auth server: ", host, ":", port);
|
||
|
|
|
||
|
|
socket = std::make_unique<network::TCPSocket>();
|
||
|
|
|
||
|
|
// Set up packet callback
|
||
|
|
socket->setPacketCallback([this](const network::Packet& packet) {
|
||
|
|
// Create a mutable copy for handling
|
||
|
|
network::Packet mutablePacket = packet;
|
||
|
|
handlePacket(mutablePacket);
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!socket->connect(host, port)) {
|
||
|
|
LOG_ERROR("Failed to connect to auth server");
|
||
|
|
setState(AuthState::FAILED);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
setState(AuthState::CONNECTED);
|
||
|
|
LOG_INFO("Connected to auth server");
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void AuthHandler::disconnect() {
|
||
|
|
if (socket) {
|
||
|
|
socket->disconnect();
|
||
|
|
socket.reset();
|
||
|
|
}
|
||
|
|
setState(AuthState::DISCONNECTED);
|
||
|
|
LOG_INFO("Disconnected from auth server");
|
||
|
|
}
|
||
|
|
|
||
|
|
bool AuthHandler::isConnected() const {
|
||
|
|
return socket && socket->isConnected();
|
||
|
|
}
|
||
|
|
|
||
|
|
void AuthHandler::requestRealmList() {
|
||
|
|
if (!isConnected()) {
|
||
|
|
LOG_ERROR("Cannot request realm list: not connected to auth server");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (state != AuthState::AUTHENTICATED) {
|
||
|
|
LOG_ERROR("Cannot request realm list: not authenticated (state: ", (int)state, ")");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
LOG_INFO("Requesting realm list");
|
||
|
|
sendRealmListRequest();
|
||
|
|
}
|
||
|
|
|
||
|
|
void AuthHandler::authenticate(const std::string& user, const std::string& pass) {
|
||
|
|
if (!isConnected()) {
|
||
|
|
LOG_ERROR("Cannot authenticate: not connected to auth server");
|
||
|
|
fail("Not connected");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (state != AuthState::CONNECTED) {
|
||
|
|
LOG_ERROR("Cannot authenticate: invalid state");
|
||
|
|
fail("Invalid state");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
LOG_INFO("Starting authentication for user: ", user);
|
||
|
|
|
||
|
|
username = user;
|
||
|
|
password = pass;
|
||
|
|
|
||
|
|
// Initialize SRP
|
||
|
|
srp = std::make_unique<SRP>();
|
||
|
|
srp->initialize(username, password);
|
||
|
|
|
||
|
|
// Send LOGON_CHALLENGE
|
||
|
|
sendLogonChallenge();
|
||
|
|
}
|
||
|
|
|
||
|
|
void AuthHandler::sendLogonChallenge() {
|
||
|
|
LOG_DEBUG("Sending LOGON_CHALLENGE");
|
||
|
|
|
||
|
|
auto packet = LogonChallengePacket::build(username, clientInfo);
|
||
|
|
socket->send(packet);
|
||
|
|
|
||
|
|
setState(AuthState::CHALLENGE_SENT);
|
||
|
|
}
|
||
|
|
|
||
|
|
void AuthHandler::handleLogonChallengeResponse(network::Packet& packet) {
|
||
|
|
LOG_DEBUG("Handling LOGON_CHALLENGE response");
|
||
|
|
|
||
|
|
LogonChallengeResponse response;
|
||
|
|
if (!LogonChallengeResponseParser::parse(packet, response)) {
|
||
|
|
fail("Failed to parse LOGON_CHALLENGE response");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!response.isSuccess()) {
|
||
|
|
fail(std::string("LOGON_CHALLENGE failed: ") + getAuthResultString(response.result));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Feed SRP with server challenge data
|
||
|
|
srp->feed(response.B, response.g, response.N, response.salt);
|
||
|
|
|
||
|
|
setState(AuthState::CHALLENGE_RECEIVED);
|
||
|
|
|
||
|
|
// Send LOGON_PROOF immediately
|
||
|
|
sendLogonProof();
|
||
|
|
}
|
||
|
|
|
||
|
|
void AuthHandler::sendLogonProof() {
|
||
|
|
LOG_DEBUG("Sending LOGON_PROOF");
|
||
|
|
|
||
|
|
auto A = srp->getA();
|
||
|
|
auto M1 = srp->getM1();
|
||
|
|
|
||
|
|
auto packet = LogonProofPacket::build(A, M1);
|
||
|
|
socket->send(packet);
|
||
|
|
|
||
|
|
setState(AuthState::PROOF_SENT);
|
||
|
|
}
|
||
|
|
|
||
|
|
void AuthHandler::handleLogonProofResponse(network::Packet& packet) {
|
||
|
|
LOG_DEBUG("Handling LOGON_PROOF response");
|
||
|
|
|
||
|
|
LogonProofResponse response;
|
||
|
|
if (!LogonProofResponseParser::parse(packet, response)) {
|
||
|
|
fail("Failed to parse LOGON_PROOF response");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!response.isSuccess()) {
|
||
|
|
fail("LOGON_PROOF failed: invalid proof");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Verify server proof
|
||
|
|
if (!srp->verifyServerProof(response.M2)) {
|
||
|
|
fail("Server proof verification failed");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Authentication successful!
|
||
|
|
sessionKey = srp->getSessionKey();
|
||
|
|
setState(AuthState::AUTHENTICATED);
|
||
|
|
|
||
|
|
LOG_INFO("========================================");
|
||
|
|
LOG_INFO(" AUTHENTICATION SUCCESSFUL!");
|
||
|
|
LOG_INFO("========================================");
|
||
|
|
LOG_INFO("User: ", username);
|
||
|
|
LOG_INFO("Session key size: ", sessionKey.size(), " bytes");
|
||
|
|
|
||
|
|
if (onSuccess) {
|
||
|
|
onSuccess(sessionKey);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void AuthHandler::sendRealmListRequest() {
|
||
|
|
LOG_DEBUG("Sending REALM_LIST request");
|
||
|
|
|
||
|
|
auto packet = RealmListPacket::build();
|
||
|
|
socket->send(packet);
|
||
|
|
|
||
|
|
setState(AuthState::REALM_LIST_REQUESTED);
|
||
|
|
}
|
||
|
|
|
||
|
|
void AuthHandler::handleRealmListResponse(network::Packet& packet) {
|
||
|
|
LOG_DEBUG("Handling REALM_LIST response");
|
||
|
|
|
||
|
|
RealmListResponse response;
|
||
|
|
if (!RealmListResponseParser::parse(packet, response)) {
|
||
|
|
LOG_ERROR("Failed to parse REALM_LIST response");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
realms = response.realms;
|
||
|
|
setState(AuthState::REALM_LIST_RECEIVED);
|
||
|
|
|
||
|
|
LOG_INFO("========================================");
|
||
|
|
LOG_INFO(" REALM LIST RECEIVED!");
|
||
|
|
LOG_INFO("========================================");
|
||
|
|
LOG_INFO("Total realms: ", realms.size());
|
||
|
|
|
||
|
|
for (size_t i = 0; i < realms.size(); ++i) {
|
||
|
|
const auto& realm = realms[i];
|
||
|
|
LOG_INFO("Realm ", (i + 1), ": ", realm.name);
|
||
|
|
LOG_INFO(" Address: ", realm.address);
|
||
|
|
LOG_INFO(" ID: ", (int)realm.id);
|
||
|
|
LOG_INFO(" Population: ", realm.population);
|
||
|
|
LOG_INFO(" Characters: ", (int)realm.characters);
|
||
|
|
if (realm.hasVersionInfo()) {
|
||
|
|
LOG_INFO(" Version: ", (int)realm.majorVersion, ".",
|
||
|
|
(int)realm.minorVersion, ".", (int)realm.patchVersion,
|
||
|
|
" (build ", realm.build, ")");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (onRealmList) {
|
||
|
|
onRealmList(realms);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void AuthHandler::handlePacket(network::Packet& packet) {
|
||
|
|
if (packet.getSize() < 1) {
|
||
|
|
LOG_WARNING("Received empty packet");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Read opcode
|
||
|
|
uint8_t opcodeValue = packet.readUInt8();
|
||
|
|
// Note: packet now has read position advanced past opcode
|
||
|
|
|
||
|
|
AuthOpcode opcode = static_cast<AuthOpcode>(opcodeValue);
|
||
|
|
|
||
|
|
LOG_DEBUG("Received auth packet, opcode: 0x", std::hex, (int)opcodeValue, std::dec);
|
||
|
|
|
||
|
|
switch (opcode) {
|
||
|
|
case AuthOpcode::LOGON_CHALLENGE:
|
||
|
|
if (state == AuthState::CHALLENGE_SENT) {
|
||
|
|
handleLogonChallengeResponse(packet);
|
||
|
|
} else {
|
||
|
|
LOG_WARNING("Unexpected LOGON_CHALLENGE response in state: ", (int)state);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case AuthOpcode::LOGON_PROOF:
|
||
|
|
if (state == AuthState::PROOF_SENT) {
|
||
|
|
handleLogonProofResponse(packet);
|
||
|
|
} else {
|
||
|
|
LOG_WARNING("Unexpected LOGON_PROOF response in state: ", (int)state);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case AuthOpcode::REALM_LIST:
|
||
|
|
if (state == AuthState::REALM_LIST_REQUESTED) {
|
||
|
|
handleRealmListResponse(packet);
|
||
|
|
} else {
|
||
|
|
LOG_WARNING("Unexpected REALM_LIST response in state: ", (int)state);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
LOG_WARNING("Unhandled auth opcode: 0x", std::hex, (int)opcodeValue, std::dec);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void AuthHandler::update(float /*deltaTime*/) {
|
||
|
|
if (!socket) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update socket (processes incoming data and calls packet callback)
|
||
|
|
socket->update();
|
||
|
|
}
|
||
|
|
|
||
|
|
void AuthHandler::setState(AuthState newState) {
|
||
|
|
if (state != newState) {
|
||
|
|
LOG_DEBUG("Auth state: ", (int)state, " -> ", (int)newState);
|
||
|
|
state = newState;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void AuthHandler::fail(const std::string& reason) {
|
||
|
|
LOG_ERROR("Authentication failed: ", reason);
|
||
|
|
setState(AuthState::FAILED);
|
||
|
|
|
||
|
|
if (onFailure) {
|
||
|
|
onFailure(reason);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace auth
|
||
|
|
} // namespace wowee
|