From 824d563fd2234443ee3d18cd76faf885aa23fde8 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Mon, 8 Sep 2025 21:45:33 -0500 Subject: [PATCH] WIP --- dNet/AuthPackets.cpp | 616 +++++++++++++++++++++++++------------------ dNet/AuthPackets.h | 208 ++++++++++++++- 2 files changed, 567 insertions(+), 257 deletions(-) diff --git a/dNet/AuthPackets.cpp b/dNet/AuthPackets.cpp index a46ed9f1..4a9edc9e 100644 --- a/dNet/AuthPackets.cpp +++ b/dNet/AuthPackets.cpp @@ -24,289 +24,393 @@ #include "MessageType/Master.h" #include "eGameMasterLevel.h" #include "StringifiedEnum.h" + namespace { std::vector 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(codestring); +namespace AuthPackets { + std::map()>> g_Handlers = { + {eMessageID::HandshakeRequest, []() { + return std::make_unique(); + }}, + {eMessageID::LoginRequest, []() { + return std::make_unique(); + }}, + }; - 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(clientNetVersionString).value_or(171022); + + bitStream.Write(clientNetVersion); + bitStream.Write(861228100); + bitStream.Write(static_cast(serverType)); + bitStream.Write(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 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 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(Game::config->GetValue("version_major")).value_or(ClientVersion::major); + const uint16_t version_current = + GeneralUtils::TryParse(Game::config->GetValue("version_current")).value_or(ClientVersion::current); + const uint16_t version_minor = + GeneralUtils::TryParse(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(); + 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(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(false); // Just upgraded from F2P + bitStream.Write(false); // User is F2P + bitStream.Write(0); // Time Remaining in F2P + + // Write custom error message + bitStream.Write(errorMsg.length()); + bitStream.Write(LUWString(errorMsg, static_cast(errorMsg.length()))); + + mutableStamps.emplace_back(eStamps::PASSPORT_AUTH_WORLD_COMMUNICATION_FINISH, 1); + + bitStream.Write((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(); + 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(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(clientNetVersionString).value_or(171022); - - bitStream.Write(clientNetVersion); - bitStream.Write(861228100); - bitStream.Write(static_cast(serverType)); - bitStream.Write(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 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& 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(Game::config->GetValue("version_major")).value_or(ClientVersion::major); - const uint16_t version_current = - GeneralUtils::TryParse(Game::config->GetValue("version_current")).value_or(ClientVersion::current); - const uint16_t version_minor = - GeneralUtils::TryParse(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(); - 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(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(false); // Just upgraded from F2P - loginResponse.Write(false); // User is F2P - loginResponse.Write(0); // Time Remaining in F2P - - // Write custom error message - loginResponse.Write(errorMsg.length()); - loginResponse.Write(LUWString(errorMsg, static_cast(errorMsg.length()))); - - stamps.emplace_back(eStamps::PASSPORT_AUTH_WORLD_COMMUNICATION_FINISH, 1); - - loginResponse.Write((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); } } + + diff --git a/dNet/AuthPackets.h b/dNet/AuthPackets.h index 156862eb..d574ec67 100644 --- a/dNet/AuthPackets.h +++ b/dNet/AuthPackets.h @@ -5,11 +5,24 @@ #include "dCommonVars.h" #include "dNetCommon.h" #include "magic_enum.hpp" +#include "BitStream.h" +#include "RakNetTypes.h" +#include "BitStreamUtils.h" +#include +#include 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 { 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 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& 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& stamps); - void LoadClaimCodes(); + void HandleAuth(RakNet::BitStream& inStream, const SystemAddress& sysAddr, dServer* server); + void LoadClaimCodes(); } #endif // AUTHPACKETS_H