This commit is contained in:
Aaron Kimbrell
2025-09-08 21:45:33 -05:00
parent 566a18df38
commit 824d563fd2
2 changed files with 567 additions and 257 deletions

View File

@@ -24,289 +24,393 @@
#include "MessageType/Master.h"
#include "eGameMasterLevel.h"
#include "StringifiedEnum.h"
namespace {
std::vector<uint32_t> claimCodes;
}
void Stamp::Serialize(RakNet::BitStream& outBitStream){
void Stamp::Serialize(RakNet::BitStream& outBitStream) {
outBitStream.Write(type);
outBitStream.Write(value);
outBitStream.Write(timestamp);
};
void AuthPackets::LoadClaimCodes() {
if(!claimCodes.empty()) return;
auto rcstring = Game::config->GetValue("rewardcodes");
auto codestrings = GeneralUtils::SplitString(rcstring, ',');
for(auto const &codestring: codestrings){
const auto code = GeneralUtils::TryParse<uint32_t>(codestring);
namespace AuthPackets {
std::map<eMessageID, std::function<std::unique_ptr<AuthLUBitStream>()>> g_Handlers = {
{eMessageID::HandshakeRequest, []() {
return std::make_unique<HandshakeRequest>();
}},
{eMessageID::LoginRequest, []() {
return std::make_unique<LoginRequest>();
}},
};
if (code && code.value() != -1) claimCodes.push_back(code.value());
void AuthLUBitStream::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(messageID);
}
bool AuthLUBitStream::Deserialize(RakNet::BitStream& bitstream) {
VALIDATE_READ(bitstream.Read(messageID));
return true;
}
bool HandshakeRequest::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(clientVersion));
bitStream.IgnoreBytes(4);
VALIDATE_READ(bitStream.Read(serviceType));
if (serviceType != ServiceType::CLIENT) LOG("WARNING: Service is not a Client!");
bitStream.IgnoreBytes(2);
VALIDATE_READ(bitStream.Read(processID));
VALIDATE_READ(bitStream.Read(port));
if (port != sysAddr.port) LOG("WARNING: Port written in packet does not match the port the client is connecting over!");
bitStream.IgnoreBytes(33);
return true;
}
void HandshakeRequest::Handle() {
LOG_DEBUG("Client Data [Version: %i, Service: %s, Process: %u, Port: %u, Sysaddr Port: %u]",
clientVersion, StringifiedEnum::ToString(serviceType).data(), processID, port, sysAddr.port);
HandshakeResponse response(server->GetIP(), server->GetPort(), server->GetServerType());
response.server = server;
response.Send(sysAddr);
}
void HandshakeResponse::Serialize(RakNet::BitStream& bitStream) const {
BitStreamUtils::WriteHeader(bitStream, ServiceType::COMMON, MessageType::Server::VERSION_CONFIRM);
const auto& clientNetVersionString = Game::config->GetValue("client_net_version");
const uint32_t clientNetVersion = GeneralUtils::TryParse<uint32_t>(clientNetVersionString).value_or(171022);
bitStream.Write<uint32_t>(clientNetVersion);
bitStream.Write<uint32_t>(861228100);
bitStream.Write(static_cast<uint32_t>(serverType));
bitStream.Write<uint64_t>(219818307120);
}
std::string CleanReceivedString(const std::string& str) {
std::string toReturn = str;
const auto removed = std::ranges::find_if(toReturn, [](unsigned char c) { return isprint(c) == 0 && isblank(c) == 0; });
toReturn.erase(removed, toReturn.end());
return toReturn;
}
bool LoginRequest::Deserialize(RakNet::BitStream& bitStream) {
LUWString usernameLUString;
VALIDATE_READ(bitStream.Read(usernameLUString));
username = usernameLUString.GetAsString();
LUWString passwordLUString(41);
VALIDATE_READ(bitStream.Read(passwordLUString));
password = passwordLUString.GetAsString();
VALIDATE_READ(bitStream.Read(locale_id));
LOG_DEBUG("Locale ID: %s", StringifiedEnum::ToString(locale_id).data());
VALIDATE_READ(bitStream.Read(clientOS));
LOG_DEBUG("Operating System: %s", StringifiedEnum::ToString(clientOS).data());
LUWString memoryStatsLU(256);
VALIDATE_READ(bitStream.Read(memoryStatsLU));
computerInfo.memoryStats = CleanReceivedString(memoryStatsLU.GetAsString());
LOG_DEBUG("Memory Stats [%s]", computerInfo.memoryStats.c_str());
LUWString videoCardLU(128);
VALIDATE_READ(bitStream.Read(videoCardLU));
computerInfo.videoCard = CleanReceivedString(videoCardLU.GetAsString());
LOG_DEBUG("VideoCard Info: [%s]", computerInfo.videoCard.c_str());
// Processor/CPU info
VALIDATE_READ(bitStream.Read(computerInfo.processorInfo.count));
VALIDATE_READ(bitStream.Read(computerInfo.processorInfo.type));
VALIDATE_READ(bitStream.Read(computerInfo.processorInfo.level));
VALIDATE_READ(bitStream.Read(computerInfo.processorInfo.revision));
LOG_DEBUG("CPU Info: [#Processors: %i, Processor Type: %s, Processor Level: %s, Processor Revision: %s]",
computerInfo.processorInfo.count,
StringifiedEnum::ToString(computerInfo.processorInfo.type).data(),
StringifiedEnum::ToString(computerInfo.processorInfo.level).data(),
StringifiedEnum::ToString(computerInfo.processorInfo.revision).data()
);
// OS Info
VALIDATE_READ(bitStream.Read(computerInfo.osVersionInfo.infoSize));
VALIDATE_READ(bitStream.Read(computerInfo.osVersionInfo.version));
VALIDATE_READ(bitStream.Read(computerInfo.osVersionInfo.buildNumber));
VALIDATE_READ(bitStream.Read(computerInfo.osVersionInfo.platformID));
LOG_DEBUG("OS Info: [Size: %i, Version: %s, Buid#: %s, platformID: %s]",
computerInfo.osVersionInfo.infoSize,
StringifiedEnum::ToString(computerInfo.osVersionInfo.version).data(),
StringifiedEnum::ToString(computerInfo.osVersionInfo.buildNumber).data(),
StringifiedEnum::ToString(computerInfo.osVersionInfo.platformID).data()
);
return true;
}
void LoginRequest::Handle() {
std::vector<Stamp> stamps;
stamps.emplace_back(eStamps::PASSPORT_AUTH_START, 0);
stamps.emplace_back(eStamps::PASSPORT_AUTH_CLIENT_OS, 0);
// Fetch account details
auto accountInfo = Database::Get()->GetAccountInfo(username);
if (!accountInfo) {
LOG("No user by name %s found!", username.c_str());
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
LoginResponse response(eLoginResponse::INVALID_USER, "", "", 2001, username, stamps);
response.server = server;
response.Send(sysAddr);
return;
}
//If we aren't running in live mode, then only GMs are allowed to enter:
const auto& closedToNonDevs = Game::config->GetValue("closed_to_non_devs");
if (closedToNonDevs.size() > 0 && bool(std::stoi(closedToNonDevs)) && accountInfo->maxGmLevel == eGameMasterLevel::CIVILIAN) {
stamps.emplace_back(eStamps::GM_REQUIRED, 1);
LoginResponse response(eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "The server is currently only open to developers.", "", 2001, username, stamps);
response.server = server;
response.Send(sysAddr);
return;
}
if (Game::config->GetValue("dont_use_keys") != "1" && accountInfo->maxGmLevel == eGameMasterLevel::CIVILIAN) {
//Check to see if we have a play key:
if (accountInfo->playKeyId == 0) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
LoginResponse response(eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username, stamps);
response.server = server;
response.Send(sysAddr);
LOG("User %s tried to log in, but they don't have a play key.", username.c_str());
return;
}
//Check if the play key is _valid_:
auto playKeyStatus = Database::Get()->IsPlaykeyActive(accountInfo->playKeyId);
if (!playKeyStatus) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
LoginResponse response(eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a valid play key associated with it!", "", 2001, username, stamps);
response.server = server;
response.Send(sysAddr);
return;
}
if (!playKeyStatus.value()) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
LoginResponse response(eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your play key has been disabled.", "", 2001, username, stamps);
response.server = server;
response.Send(sysAddr);
LOG("User %s tried to log in, but their play key was disabled", username.c_str());
return;
}
} else if (Game::config->GetValue("dont_use_keys") == "1" || accountInfo->maxGmLevel > eGameMasterLevel::CIVILIAN) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_BYPASS, 1);
}
if (accountInfo->banned) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
LoginResponse response(eLoginResponse::BANNED, "", "", 2001, username, stamps);
response.server = server;
response.Send(sysAddr);
return;
}
if (accountInfo->locked) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
LoginResponse response(eLoginResponse::ACCOUNT_LOCKED, "", "", 2001, username, stamps);
response.server = server;
response.Send(sysAddr);
return;
}
bool loginSuccess = ::bcrypt_checkpw(password.c_str(), accountInfo->bcryptPassword.c_str()) == 0;
if (!loginSuccess) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
LoginResponse response(eLoginResponse::WRONG_PASS, "", "", 2001, username, stamps);
response.server = server;
response.Send(sysAddr);
LOG("Wrong password used");
} else {
SystemAddress system = sysAddr; //Copy the sysAddr
auto serverPtr = server;
auto usernameCopy = username;
ZoneInstanceManager::Instance()->RequestZoneTransfer(server, 0, 0, false, [system, serverPtr, usernameCopy, stamps](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string zoneIP, uint16_t zonePort) mutable {
LoginResponse response(eLoginResponse::SUCCESS, "", zoneIP, zonePort, usernameCopy, stamps);
response.server = serverPtr;
response.Send(system);
return;
}
);
for (auto const code : claimCodes) {
Database::Get()->InsertRewardCode(accountInfo->id, code);
}
}
}
void LoginResponse::Serialize(RakNet::BitStream& bitStream) const {
std::vector<Stamp> mutableStamps = stamps;
mutableStamps.emplace_back(eStamps::PASSPORT_AUTH_IM_LOGIN_START, 1);
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::LOGIN_RESPONSE);
bitStream.Write(responseCode);
// Event Gating
bitStream.Write(LUString(Game::config->GetValue("event_1")));
bitStream.Write(LUString(Game::config->GetValue("event_2")));
bitStream.Write(LUString(Game::config->GetValue("event_3")));
bitStream.Write(LUString(Game::config->GetValue("event_4")));
bitStream.Write(LUString(Game::config->GetValue("event_5")));
bitStream.Write(LUString(Game::config->GetValue("event_6")));
bitStream.Write(LUString(Game::config->GetValue("event_7")));
bitStream.Write(LUString(Game::config->GetValue("event_8")));
const uint16_t version_major =
GeneralUtils::TryParse<uint16_t>(Game::config->GetValue("version_major")).value_or(ClientVersion::major);
const uint16_t version_current =
GeneralUtils::TryParse<uint16_t>(Game::config->GetValue("version_current")).value_or(ClientVersion::current);
const uint16_t version_minor =
GeneralUtils::TryParse<uint16_t>(Game::config->GetValue("version_minor")).value_or(ClientVersion::minor);
bitStream.Write(version_major);
bitStream.Write(version_current);
bitStream.Write(version_minor);
// Writes the user key
uint32_t sessionKey = GeneralUtils::GenerateRandomNumber<uint32_t>();
std::string userHash = std::to_string(sessionKey);
userHash = md5(userHash);
bitStream.Write(LUWString(userHash));
// World Server IP
bitStream.Write(LUString(wServerIP));
// Chat Server IP (unused)
bitStream.Write(LUString(""));
// World Server Redirect port
bitStream.Write(wServerPort);
// Char Server Redirect port (unused)
bitStream.Write(static_cast<uint16_t>(0));
// CDN Key
bitStream.Write(LUString(""));
// CDN Ticket
bitStream.Write(LUString("00000000-0000-0000-0000-000000000000", 37));
// Language
bitStream.Write(Language::en_US);
// Write the localization
bitStream.Write(LUString("US", 3));
bitStream.Write<uint8_t>(false); // Just upgraded from F2P
bitStream.Write<uint8_t>(false); // User is F2P
bitStream.Write<uint64_t>(0); // Time Remaining in F2P
// Write custom error message
bitStream.Write<uint16_t>(errorMsg.length());
bitStream.Write(LUWString(errorMsg, static_cast<uint32_t>(errorMsg.length())));
mutableStamps.emplace_back(eStamps::PASSPORT_AUTH_WORLD_COMMUNICATION_FINISH, 1);
bitStream.Write<uint32_t>((sizeof(Stamp) * mutableStamps.size()) + sizeof(uint32_t));
for (auto& stamp : mutableStamps) stamp.Serialize(bitStream);
//Inform the master server that we've created a session for this user:
if (responseCode == eLoginResponse::SUCCESS && server) {
uint32_t masterSessionKey = GeneralUtils::GenerateRandomNumber<uint32_t>();
RakNet::BitStream masterBitStream;
BitStreamUtils::WriteHeader(masterBitStream, ServiceType::MASTER, MessageType::Master::SET_SESSION_KEY);
masterBitStream.Write(masterSessionKey);
masterBitStream.Write(LUString(username));
server->SendToMaster(masterBitStream);
LOG("Set sessionKey: %i for user %s", masterSessionKey, username.c_str());
}
}
void LoadClaimCodes() {
if(!claimCodes.empty()) return;
auto rcstring = Game::config->GetValue("rewardcodes");
auto codestrings = GeneralUtils::SplitString(rcstring, ',');
for(auto const &codestring: codestrings){
const auto code = GeneralUtils::TryParse<uint32_t>(codestring);
if (code && code.value() != -1) claimCodes.push_back(code.value());
}
}
}
// Legacy wrapper functions for backward compatibility
void AuthPackets::HandleHandshake(dServer* server, Packet* packet) {
CINSTREAM_SKIP_HEADER
uint32_t clientVersion = 0;
inStream.Read(clientVersion);
inStream.IgnoreBytes(4);
ServiceType serviceType;
inStream.Read(serviceType);
if (serviceType != ServiceType::CLIENT) LOG("WARNING: Service is not a Client!");
inStream.IgnoreBytes(2);
uint32_t processID;
inStream.Read(processID);
uint16_t port;
inStream.Read(port);
if (port != packet->systemAddress.port) LOG("WARNING: Port written in packet does not match the port the client is connecting over!");
inStream.IgnoreBytes(33);
LOG_DEBUG("Client Data [Version: %i, Service: %s, Process: %u, Port: %u, Sysaddr Port: %u]", clientVersion, StringifiedEnum::ToString(serviceType).data(), processID, port, packet->systemAddress.port);
SendHandshake(server, packet->systemAddress, server->GetIP(), server->GetPort(), server->GetServerType());
CINSTREAM_SKIP_HEADER;
HandshakeRequest request;
request.sysAddr = packet->systemAddress;
request.server = server;
if (request.Deserialize(inStream)) {
request.Handle();
} else {
LOG_DEBUG("Error Reading Handshake Request");
}
}
void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, const std::string& nextServerIP, uint16_t nextServerPort, const ServiceType serverType) {
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, ServiceType::COMMON, MessageType::Server::VERSION_CONFIRM);
const auto& clientNetVersionString = Game::config->GetValue("client_net_version");
const uint32_t clientNetVersion = GeneralUtils::TryParse<uint32_t>(clientNetVersionString).value_or(171022);
bitStream.Write<uint32_t>(clientNetVersion);
bitStream.Write<uint32_t>(861228100);
bitStream.Write(static_cast<uint32_t>(serverType));
bitStream.Write<uint64_t>(219818307120);
server->Send(bitStream, sysAddr, false);
}
std::string CleanReceivedString(const std::string& str) {
std::string toReturn = str;
const auto removed = std::ranges::find_if(toReturn, [](unsigned char c) { return isprint(c) == 0 && isblank(c) == 0; });
toReturn.erase(removed, toReturn.end());
return toReturn;
HandshakeResponse response(nextServerIP, nextServerPort, serverType);
response.server = server;
response.Send(sysAddr);
}
void AuthPackets::HandleLoginRequest(dServer* server, Packet* packet) {
CINSTREAM_SKIP_HEADER;
std::vector<Stamp> stamps;
stamps.emplace_back(eStamps::PASSPORT_AUTH_START, 0);
LUWString usernameLUString;
inStream.Read(usernameLUString);
const auto username = usernameLUString.GetAsString();
LUWString password(41);
inStream.Read(password);
LanguageCodeID locale_id;
inStream.Read(locale_id);
LOG_DEBUG("Locale ID: %s", StringifiedEnum::ToString(locale_id).data());
ClientOS clientOS;
inStream.Read(clientOS);
LOG_DEBUG("Operating System: %s", StringifiedEnum::ToString(clientOS).data());
stamps.emplace_back(eStamps::PASSPORT_AUTH_CLIENT_OS, 0);
LUWString memoryStats(256);
inStream.Read(memoryStats);
LOG_DEBUG("Memory Stats [%s]", CleanReceivedString(memoryStats.GetAsString()).c_str());
LUWString videoCard(128);
inStream.Read(videoCard);
LOG_DEBUG("VideoCard Info: [%s]", CleanReceivedString(videoCard.GetAsString()).c_str());
// Processor/CPU info
uint32_t numOfProcessors;
inStream.Read(numOfProcessors);
uint32_t processorType;
inStream.Read(processorType);
uint16_t processorLevel;
inStream.Read(processorLevel);
uint16_t processorRevision;
inStream.Read(processorRevision);
LOG_DEBUG("CPU Info: [#Processors: %i, Processor Type: %i, Processor Level: %i, Processor Revision: %i]", numOfProcessors, processorType, processorLevel, processorRevision);
// OS Info
uint32_t osVersionInfoSize;
inStream.Read(osVersionInfoSize);
uint32_t majorVersion;
inStream.Read(majorVersion);
uint32_t minorVersion;
inStream.Read(minorVersion);
uint32_t buildNumber;
inStream.Read(buildNumber);
uint32_t platformID;
inStream.Read(platformID);
LOG_DEBUG("OS Info: [Size: %i, Major: %i, Minor %i, Buid#: %i, platformID: %i]", osVersionInfoSize, majorVersion, minorVersion, buildNumber, platformID);
// Fetch account details
auto accountInfo = Database::Get()->GetAccountInfo(username);
if (!accountInfo) {
LOG("No user by name %s found!", username.c_str());
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::INVALID_USER, "", "", 2001, username, stamps);
return;
}
//If we aren't running in live mode, then only GMs are allowed to enter:
const auto& closedToNonDevs = Game::config->GetValue("closed_to_non_devs");
if (closedToNonDevs.size() > 0 && bool(std::stoi(closedToNonDevs)) && accountInfo->maxGmLevel == eGameMasterLevel::CIVILIAN) {
stamps.emplace_back(eStamps::GM_REQUIRED, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "The server is currently only open to developers.", "", 2001, username, stamps);
return;
}
if (Game::config->GetValue("dont_use_keys") != "1" && accountInfo->maxGmLevel == eGameMasterLevel::CIVILIAN) {
//Check to see if we have a play key:
if (accountInfo->playKeyId == 0) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username, stamps);
LOG("User %s tried to log in, but they don't have a play key.", username.c_str());
return;
}
//Check if the play key is _valid_:
auto playKeyStatus = Database::Get()->IsPlaykeyActive(accountInfo->playKeyId);
if (!playKeyStatus) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a valid play key associated with it!", "", 2001, username, stamps);
return;
}
if (!playKeyStatus.value()) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your play key has been disabled.", "", 2001, username, stamps);
LOG("User %s tried to log in, but their play key was disabled", username.c_str());
return;
}
} else if (Game::config->GetValue("dont_use_keys") == "1" || accountInfo->maxGmLevel > eGameMasterLevel::CIVILIAN){
stamps.emplace_back(eStamps::PASSPORT_AUTH_BYPASS, 1);
}
if (accountInfo->banned) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::BANNED, "", "", 2001, username, stamps);
return;
}
if (accountInfo->locked) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::ACCOUNT_LOCKED, "", "", 2001, username, stamps);
return;
}
bool loginSuccess = ::bcrypt_checkpw(password.GetAsString().c_str(), accountInfo->bcryptPassword.c_str()) == 0;
if (!loginSuccess) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::WRONG_PASS, "", "", 2001, username, stamps);
LOG("Wrong password used");
LoginRequest request;
request.sysAddr = packet->systemAddress;
request.server = server;
if (request.Deserialize(inStream)) {
request.Handle();
} else {
SystemAddress system = packet->systemAddress; //Copy the sysAddr before the Packet gets destroyed from main
if (!server->GetIsConnectedToMaster()) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_WORLD_DISCONNECT, 1);
AuthPackets::SendLoginResponse(server, system, eLoginResponse::GENERAL_FAILED, "", "", 0, username, stamps);
return;
}
stamps.emplace_back(eStamps::PASSPORT_AUTH_WORLD_SESSION_CONFIRM_TO_AUTH, 1);
ZoneInstanceManager::Instance()->RequestZoneTransfer(server, 0, 0, false, [system, server, username, stamps](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string zoneIP, uint16_t zonePort) mutable {
AuthPackets::SendLoginResponse(server, system, eLoginResponse::SUCCESS, "", zoneIP, zonePort, username, stamps);
});
}
for(auto const code: claimCodes){
Database::Get()->InsertRewardCode(accountInfo->id, code);
LOG_DEBUG("Error Reading Login Request");
}
}
void AuthPackets::SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username, std::vector<Stamp>& stamps) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_IM_LOGIN_START, 1);
RakNet::BitStream loginResponse;
BitStreamUtils::WriteHeader(loginResponse, ServiceType::CLIENT, MessageType::Client::LOGIN_RESPONSE);
LoginResponse response(responseCode, errorMsg, wServerIP, wServerPort, username, stamps);
response.server = server;
response.Send(sysAddr);
}
loginResponse.Write(responseCode);
// Non Struct Functions
void AuthPackets::HandleAuth(RakNet::BitStream& inStream, const SystemAddress& sysAddr, dServer* server) {
AuthLUBitStream data;
if (!data.Deserialize(inStream)) {
LOG_DEBUG("Error Reading Auth header");
return;
}
// Event Gating
loginResponse.Write(LUString(Game::config->GetValue("event_1")));
loginResponse.Write(LUString(Game::config->GetValue("event_2")));
loginResponse.Write(LUString(Game::config->GetValue("event_3")));
loginResponse.Write(LUString(Game::config->GetValue("event_4")));
loginResponse.Write(LUString(Game::config->GetValue("event_5")));
loginResponse.Write(LUString(Game::config->GetValue("event_6")));
loginResponse.Write(LUString(Game::config->GetValue("event_7")));
loginResponse.Write(LUString(Game::config->GetValue("event_8")));
const uint16_t version_major =
GeneralUtils::TryParse<uint16_t>(Game::config->GetValue("version_major")).value_or(ClientVersion::major);
const uint16_t version_current =
GeneralUtils::TryParse<uint16_t>(Game::config->GetValue("version_current")).value_or(ClientVersion::current);
const uint16_t version_minor =
GeneralUtils::TryParse<uint16_t>(Game::config->GetValue("version_minor")).value_or(ClientVersion::minor);
loginResponse.Write(version_major);
loginResponse.Write(version_current);
loginResponse.Write(version_minor);
// Writes the user key
uint32_t sessionKey = GeneralUtils::GenerateRandomNumber<uint32_t>();
std::string userHash = std::to_string(sessionKey);
userHash = md5(userHash);
loginResponse.Write(LUWString(userHash));
// World Server IP
loginResponse.Write(LUString(wServerIP));
// Chat Server IP (unused)
loginResponse.Write(LUString(""));
// World Server Redirect port
loginResponse.Write(wServerPort);
// Char Server Redirect port (unused)
loginResponse.Write(static_cast<uint16_t>(0));
// CDN Key
loginResponse.Write(LUString(""));
// CDN Ticket
loginResponse.Write(LUString("00000000-0000-0000-0000-000000000000", 37));
// Language
loginResponse.Write(Language::en_US);
// Write the localization
loginResponse.Write(LUString("US", 3));
loginResponse.Write<uint8_t>(false); // Just upgraded from F2P
loginResponse.Write<uint8_t>(false); // User is F2P
loginResponse.Write<uint64_t>(0); // Time Remaining in F2P
// Write custom error message
loginResponse.Write<uint16_t>(errorMsg.length());
loginResponse.Write(LUWString(errorMsg, static_cast<uint32_t>(errorMsg.length())));
stamps.emplace_back(eStamps::PASSPORT_AUTH_WORLD_COMMUNICATION_FINISH, 1);
loginResponse.Write<uint32_t>((sizeof(Stamp) * stamps.size()) + sizeof(uint32_t));
for (auto& stamp : stamps) stamp.Serialize(loginResponse);
server->Send(loginResponse, sysAddr, false);
//Inform the master server that we've created a session for this user:
if (responseCode == eLoginResponse::SUCCESS) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::SET_SESSION_KEY);
bitStream.Write(sessionKey);
bitStream.Write(LUString(username));
server->SendToMaster(bitStream);
LOG("Set sessionKey: %i for user %s", sessionKey, username.c_str());
auto it = g_Handlers.find(data.messageID);
if (it != g_Handlers.end()) {
auto request = it->second();
request->sysAddr = sysAddr;
request->server = server;
if (!request->Deserialize(inStream)) {
LOG_DEBUG("Error Reading Auth Request: %s", StringifiedEnum::ToString(data.messageID).data());
return;
}
LOG("Received auth message %s", StringifiedEnum::ToString(data.messageID).data());
request->Handle();
} else {
LOG_DEBUG("Unhandled Auth Request with ID: %i", data.messageID);
}
}

View File

@@ -5,11 +5,24 @@
#include "dCommonVars.h"
#include "dNetCommon.h"
#include "magic_enum.hpp"
#include "BitStream.h"
#include "RakNetTypes.h"
#include "BitStreamUtils.h"
#include <functional>
#include <memory>
enum class eLoginResponse : uint8_t;
enum class ServiceType : uint16_t;
class dServer;
enum class eMessageID : uint32_t {
HandshakeRequest = 0,
HandshakeResponse,
LoginRequest,
LoginResponse,
UnknownError
};
enum class eStamps : uint32_t {
PASSPORT_AUTH_START,
PASSPORT_AUTH_BYPASS,
@@ -78,6 +91,121 @@ enum class LanguageCodeID : uint16_t {
en_GB = 0x0809
};
// For more info on these values, go to this link
// https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
enum class ProcessorType : uint32_t {
UNKNOWN = 0,
INTEL_386 = 386, // Intel 80386
INTEL_486 = 486, // Intel 80486
PENTIUM = 586, // Intel Pentium
MIPS = 4000, // MIPS R4000
ALPHA = 21064, // Alpha 21064
PPC = 601, // PowerPC 601
SHX = 103, // SuperH SHX
INTEL_IA64 = 2200, // Intel Itanium
AMD_X8664 = 8664, // x64 (AMD or Intel EM64T)
ARM = 448, // ARM (0x01C0 in hex, 448 in decimal)
ARM64 = 43620 // ARM64 (0xAA64 in hex, 43620 in decimal)
};
enum class ProcessorLevel : uint16_t {
INTEL_386 = 3,
INTEL_486 = 4,
PENTIUM = 5,
INTEL_P6 = 6, // Pentium Pro/II/III/4 or later
ITANIUM = 0xA,
X64 = 0xE,
ARM = 0x14,
ARM64 = 0x15,
UNKNOWN = 0xFFFF
};
// ProcessorRevision is typically a 16-bit value, but we can define some common values for reference
// For x86: high byte = model, low byte = stepping
// For other architectures, refer to documentation
enum class ProcessorRevision : uint16_t {
// Intel 80386
INTEL_386_MODEL_0_STEPPING_0 = 0x0000,
INTEL_386_MODEL_0_STEPPING_1 = 0x0001,
// Intel 80486
INTEL_486_MODEL_0_STEPPING_0 = 0x0000,
INTEL_486_MODEL_0_STEPPING_1 = 0x0001,
// Intel Pentium
PENTIUM_MODEL_1_STEPPING_1 = 0x0101,
PENTIUM_MODEL_2_STEPPING_3 = 0x0203,
// Intel Pentium Pro/II/III/4
P6_MODEL_3_STEPPING_3 = 0x0303,
P6_MODEL_5_STEPPING_7 = 0x0507,
// Intel Itanium
ITANIUM_MODEL_1_STEPPING_0 = 0x0100,
// AMD x64
AMD_X64_MODEL_15_STEPPING_65 = 0x0F41,
AMD_X64_MODEL_23_STEPPING_1 = 0x1701,
// Intel x64
INTEL_X64_MODEL_6_STEPPING_10 = 0x060A,
INTEL_X64_MODEL_15_STEPPING_11 = 0x0F0B,
// ARM/ARM64 (values are typically implementation-specific)
ARM_MODEL_0_STEPPING_0 = 0x0000,
ARM64_MODEL_0_STEPPING_0 = 0x0000,
// Unknown
UNKNOWN = 0xFFFF
};
// Windows version enum based on OSVERSIONINFOEXA major/minor values
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa
enum class WindowsVersion : uint32_t {
UNKNOWN = 0,
WIN_2000 = 0x0500, // Major: 5, Minor: 0
WIN_XP = 0x0501, // Major: 5, Minor: 1
WIN_SERVER_2003 = 0x0502, // Major: 5, Minor: 2
WIN_VISTA = 0x0600, // Major: 6, Minor: 0
WIN_7 = 0x0601, // Major: 6, Minor: 1
WIN_8 = 0x0602, // Major: 6, Minor: 2
WIN_8_1 = 0x0603, // Major: 6, Minor: 3
WIN_10 = 0x0A00, // Major: 10, Minor: 0
WIN_11 = 0x0A00 // Major: 10, Minor: 0 (distinguish by build number)
};
// Windows Platform ID enum based on OSVERSIONINFOEXA documentation
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa
enum class WindowsPlatformID : uint32_t {
UNKNOWN = 0,
WIN32s = 0, // Windows 3.x
WIN32_WINDOWS = 1, // Windows 95/98/ME
WIN32_NT = 2, // Windows NT and later
WIN32_CE = 3 // Windows CE
};
// Windows Build Number enum (common values)
enum class WindowsBuildNumber : uint32_t {
UNKNOWN = 0,
WIN_2000 = 2195,
WIN_XP = 2600,
WIN_SERVER_2003 = 3790,
WIN_VISTA = 6000,
WIN_7 = 7600,
WIN_7_SP1 = 7601,
WIN_8 = 9200,
WIN_8_1 = 9600,
WIN_10_1507 = 10240,
WIN_10_1511 = 10586,
WIN_10_1607 = 14393,
WIN_10_1703 = 15063,
WIN_10_1709 = 16299,
WIN_10_1803 = 17134,
WIN_10_1809 = 17763,
WIN_10_1903 = 18362,
WIN_10_1909 = 18363,
WIN_10_2004 = 19041,
WIN_10_20H2 = 19042,
WIN_10_21H1 = 19043,
WIN_10_21H2 = 19044,
WIN_10_22H2 = 19045,
WIN_11_21H2 = 22000,
WIN_11_22H2 = 22621,
WIN_11_23H2 = 22631
};
template <>
struct magic_enum::customize::enum_range<LanguageCodeID> {
static constexpr int min = 1031;
@@ -92,13 +220,91 @@ enum class Language : uint32_t {
};
namespace AuthPackets {
struct AuthLUBitStream : public LUBitStream {
eMessageID messageID = eMessageID::UnknownError;
SystemAddress sysAddr = UNASSIGNED_SYSTEM_ADDRESS;
dServer* server = nullptr;
AuthLUBitStream() = default;
AuthLUBitStream(eMessageID _messageID) : messageID{_messageID} {};
virtual void Serialize(RakNet::BitStream& bitStream) const override;
virtual bool Deserialize(RakNet::BitStream& bitStream) override;
virtual void Handle() override {};
};
struct HandshakeRequest : public AuthLUBitStream {
uint32_t clientVersion = 0;
ServiceType serviceType;
uint32_t processID = 0;
uint16_t port = 0;
HandshakeRequest() : AuthLUBitStream(eMessageID::HandshakeRequest) {}
bool Deserialize(RakNet::BitStream& bitStream) override;
void Handle() override;
};
struct HandshakeResponse : public AuthLUBitStream {
std::string nextServerIP;
uint16_t nextServerPort = 0;
ServiceType serverType;
HandshakeResponse() : AuthLUBitStream(eMessageID::HandshakeResponse) {}
HandshakeResponse(const std::string& ip, uint16_t port, ServiceType type)
: AuthLUBitStream(eMessageID::HandshakeResponse), nextServerIP(ip), nextServerPort(port), serverType(type) {}
void Serialize(RakNet::BitStream& bitStream) const override;
};
struct LoginRequest : public AuthLUBitStream {
std::string username;
std::string password;
LanguageCodeID locale_id;
ClientOS clientOS;
struct ComputerInfo {
std::string memoryStats;
std::string videoCard;
struct ProcessorInfo {
uint32_t count = 0;
ProcessorType type = ProcessorType::UNKNOWN;
ProcessorLevel level = ProcessorLevel::UNKNOWN;
ProcessorRevision revision = ProcessorRevision::UNKNOWN;
} processorInfo;
struct OSVersionInfo {
uint32_t infoSize = 0;
WindowsVersion version = WindowsVersion::UNKNOWN;
WindowsBuildNumber buildNumber = WindowsBuildNumber::UNKNOWN;
WindowsPlatformID platformID = WindowsPlatformID::UNKNOWN;
} osVersionInfo;
} computerInfo;
LoginRequest() : AuthLUBitStream(eMessageID::LoginRequest) {}
bool Deserialize(RakNet::BitStream& bitStream) override;
void Handle() override;
};
struct LoginResponse : public AuthLUBitStream {
eLoginResponse responseCode;
std::string errorMsg;
std::string wServerIP;
uint16_t wServerPort = 0;
std::string username;
std::vector<Stamp> stamps;
LoginResponse() : AuthLUBitStream(eMessageID::LoginResponse) {}
LoginResponse(eLoginResponse code, const std::string& error, const std::string& ip, uint16_t port, const std::string& user, std::vector<Stamp>& s)
: AuthLUBitStream(eMessageID::LoginResponse), responseCode(code), errorMsg(error), wServerIP(ip), wServerPort(port), username(user), stamps(s) {}
void Serialize(RakNet::BitStream& bitStream) const override;
};
// Legacy function signatures maintained for backward compatibility
void HandleHandshake(dServer* server, Packet* packet);
void SendHandshake(dServer* server, const SystemAddress& sysAddr, const std::string& nextServerIP, uint16_t nextServerPort, const ServiceType serverType);
void HandleLoginRequest(dServer* server, Packet* packet);
void SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username, std::vector<Stamp>& stamps);
void LoadClaimCodes();
void HandleAuth(RakNet::BitStream& inStream, const SystemAddress& sysAddr, dServer* server);
void LoadClaimCodes();
}
#endif // AUTHPACKETS_H