From 325598cd99b14fc3f0d82d27a4a94c6ccba9d8a7 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Sat, 6 Jan 2024 02:16:10 -0600 Subject: [PATCH] feat: fully reading auth packets and use stamps (#1398) * feat: fully reading auth packets and use stamps * fix the stupid define * Address feedback * no more Stamp(...) --- dNet/AuthPackets.cpp | 215 +++++++++++++++++++++++++++++-------------- dNet/AuthPackets.h | 84 ++++++++++++++++- dNet/dServer.h | 8 ++ 3 files changed, 239 insertions(+), 68 deletions(-) diff --git a/dNet/AuthPackets.cpp b/dNet/AuthPackets.cpp index 7feb0cc3a..de8f57048 100644 --- a/dNet/AuthPackets.cpp +++ b/dNet/AuthPackets.cpp @@ -24,11 +24,17 @@ #include "eServerMessageType.h" #include "eMasterMessageType.h" #include "eGameMasterLevel.h" - +#include "StringifiedEnum.h" namespace { std::vector claimCodes; } +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"); @@ -42,12 +48,26 @@ void AuthPackets::LoadClaimCodes() { } void AuthPackets::HandleHandshake(dServer* server, Packet* packet) { - RakNet::BitStream inStream(packet->data, packet->length, false); - uint64_t header = inStream.Read(header); + CINSTREAM_SKIP_HEADER uint32_t clientVersion = 0; inStream.Read(clientVersion); + inStream.IgnoreBytes(4); + + ServiceId serviceId; + inStream.Read(serviceId); + if (serviceId != ServiceId::Client) LOG("WARNING: Service ID is not a Client!"); + + 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(serviceId).data(), processID, port, packet->systemAddress.port); - LOG("Received client version: %i", clientVersion); SendHandshake(server, packet->systemAddress, server->GetIP(), server->GetPort(), server->GetServerType()); } @@ -60,42 +80,93 @@ void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, c if (!clientNetVersionString.empty()) GeneralUtils::TryParse(clientNetVersionString, clientNetVersion); bitStream.Write(clientNetVersion); - bitStream.Write(0x93); + bitStream.Write(861228100); - if (serverType == ServerType::Auth) bitStream.Write(uint32_t(1)); //Conn: auth - else bitStream.Write(4); //Conn: world - - bitStream.Write(0); //Server process ID - bitStream.Write(nextServerPort); + if (serverType == ServerType::Auth) bitStream.Write(ServiceId::Auth); + else if (serverType == ServerType::World) bitStream.Write(ServiceId::World); + else bitStream.Write(ServiceId::General); + bitStream.Write(774909490); server->Send(&bitStream, sysAddr, false); } void AuthPackets::HandleLoginRequest(dServer* server, Packet* packet) { - std::string username = PacketUtils::ReadString(8, packet, true); - std::string password = PacketUtils::ReadString(0x4A, packet, true); - const char* szUsername = username.c_str(); + CINSTREAM_SKIP_HEADER; + + std::vector stamps; + stamps.emplace_back(eStamps::PASSPORT_AUTH_START, 0); + + LUWString usernameLUString(33); + 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]", memoryStats.GetAsString().c_str()); + + LUWString videoCard(128); + inStream.Read(videoCard); + LOG_DEBUG("VideoCard Info: [%s]", 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()); - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::INVALID_USER, "", "", 2001, username); + 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) { - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "The server is currently only open to developers.", "", 2001, username); + 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) { - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username); + 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; } @@ -104,40 +175,50 @@ void AuthPackets::HandleLoginRequest(dServer* server, Packet* packet) { auto playKeyStatus = Database::Get()->IsPlaykeyActive(accountInfo->playKeyId); if (!playKeyStatus) { - 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.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()) { - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your play key has been disabled.", "", 2001, username); + 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) { - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::BANNED, "", "", 2001, username); return; + stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1); + AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::BANNED, "", "", 2001, username, stamps); + return; } if (accountInfo->locked) { - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::ACCOUNT_LOCKED, "", "", 2001, username); return; + 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.c_str(), accountInfo->bcryptPassword.c_str()) == 0; + bool loginSuccess = ::bcrypt_checkpw(password.GetAsString().c_str(), accountInfo->bcryptPassword.c_str()) == 0; if (!loginSuccess) { - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::WRONG_PASS, "", "", 2001, username); + stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1); + AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::WRONG_PASS, "", "", 2001, username, stamps); LOG("Wrong password used"); } else { SystemAddress system = packet->systemAddress; //Copy the sysAddr before the Packet gets destroyed from main if (!server->GetIsConnectedToMaster()) { - AuthPackets::SendLoginResponse(server, system, eLoginResponse::GENERAL_FAILED, "", "", 0, username); + stamps.emplace_back(eStamps::PASSPORT_AUTH_WORLD_DISCONNECT, 1); + AuthPackets::SendLoginResponse(server, system, eLoginResponse::GENERAL_FAILED, "", "", 0, username, stamps); return; } - - ZoneInstanceManager::Instance()->RequestZoneTransfer(server, 0, 0, false, [system, server, username](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string zoneIP, uint16_t zonePort) { - AuthPackets::SendLoginResponse(server, system, eLoginResponse::SUCCESS, "", zoneIP, zonePort, username); + 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); }); } @@ -146,21 +227,22 @@ void AuthPackets::HandleLoginRequest(dServer* server, Packet* packet) { } } -void AuthPackets::SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username) { - RakNet::BitStream packet; - BitStreamUtils::WriteHeader(packet, eConnectionType::CLIENT, eClientMessageType::LOGIN_RESPONSE); +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, eConnectionType::CLIENT, eClientMessageType::LOGIN_RESPONSE); - packet.Write(GeneralUtils::CastUnderlyingType(responseCode)); + loginResponse.Write(GeneralUtils::CastUnderlyingType(responseCode)); // Event Gating - packet.Write(LUString(Game::config->GetValue("event_1"))); - packet.Write(LUString(Game::config->GetValue("event_2"))); - packet.Write(LUString(Game::config->GetValue("event_3"))); - packet.Write(LUString(Game::config->GetValue("event_4"))); - packet.Write(LUString(Game::config->GetValue("event_5"))); - packet.Write(LUString(Game::config->GetValue("event_6"))); - packet.Write(LUString(Game::config->GetValue("event_7"))); - packet.Write(LUString(Game::config->GetValue("event_8"))); + 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"))); uint16_t version_major = 1; uint16_t version_current = 10; @@ -169,53 +251,52 @@ void AuthPackets::SendLoginResponse(dServer* server, const SystemAddress& sysAdd GeneralUtils::TryParse(Game::config->GetValue("version_current"), version_current); GeneralUtils::TryParse(Game::config->GetValue("version_minor"), version_minor); - packet.Write(version_major); - packet.Write(version_current); - packet.Write(version_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); - packet.Write(LUWString(userHash)); + loginResponse.Write(LUWString(userHash)); - // Write the Character and Chat IPs - packet.Write(LUString(wServerIP)); - packet.Write(LUString("")); + // World Server IP + loginResponse.Write(LUString(wServerIP)); + // Chat Server IP (unused) + loginResponse.Write(LUString("")); - // Write the Character and Chat Ports - packet.Write(wServerPort); - packet.Write(0); + // World Server Redirect port + loginResponse.Write(wServerPort); + // Char Server Redirect port (unused) + loginResponse.Write(static_cast(0)); // CDN Key - packet.Write(LUString("")); + loginResponse.Write(LUString("")); // CDN Ticket - packet.Write(LUString("00000000-0000-0000-0000-000000000000", 37)); + loginResponse.Write(LUString("00000000-0000-0000-0000-000000000000", 37)); - packet.Write(0); // Language + // Language + loginResponse.Write(Language::en_US); // Write the localization - packet.Write(LUString("US", 3)); + loginResponse.Write(LUString("US", 3)); - packet.Write(false); // Just upgraded from F2P - packet.Write(false); // User is F2P - packet.Write(0); // Time Remaining in F2P + loginResponse.Write(false); // Just upgraded from F2P + loginResponse.Write(false); // User is F2P + loginResponse.Write(0); // Time Remaining in F2P // Write custom error message - packet.Write(errorMsg.length()); - packet.Write(LUWString(errorMsg, static_cast(errorMsg.length()))); - - // Here write auth logs - packet.Write(20); - for (uint32_t i = 0; i < 20; ++i) { - packet.Write(8); - packet.Write(44); - packet.Write(14000); - packet.Write(0); - } + 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(&packet, sysAddr, false); + server->Send(&loginResponse, sysAddr, false); //Inform the master server that we've created a session for this user: if (responseCode == eLoginResponse::SUCCESS) { diff --git a/dNet/AuthPackets.h b/dNet/AuthPackets.h index eb275a463..539bae75a 100644 --- a/dNet/AuthPackets.h +++ b/dNet/AuthPackets.h @@ -4,17 +4,99 @@ #define _VARIADIC_MAX 10 #include "dCommonVars.h" #include "dNetCommon.h" +#include "magic_enum.hpp" enum class ServerType : uint32_t; enum class eLoginResponse : uint8_t; class dServer; +enum class eStamps : uint32_t { + PASSPORT_AUTH_START, + PASSPORT_AUTH_BYPASS, + PASSPORT_AUTH_ERROR, + PASSPORT_AUTH_DB_SELECT_START, + PASSPORT_AUTH_DB_SELECT_FINISH, + PASSPORT_AUTH_DB_INSERT_START, + PASSPORT_AUTH_DB_INSERT_FINISH, + PASSPORT_AUTH_LEGOINT_COMMUNICATION_START, + PASSPORT_AUTH_LEGOINT_RECEIVED, + PASSPORT_AUTH_LEGOINT_THREAD_SPAWN, + PASSPORT_AUTH_LEGOINT_WEBSERVICE_START, + PASSPORT_AUTH_LEGOINT_WEBSERVICE_FINISH, + PASSPORT_AUTH_LEGOINT_LEGOCLUB_START, + PASSPORT_AUTH_LEGOINT_LEGOCLUB_FINISH, + PASSPORT_AUTH_LEGOINT_THREAD_FINISH, + PASSPORT_AUTH_LEGOINT_REPLY, + PASSPORT_AUTH_LEGOINT_ERROR, + PASSPORT_AUTH_LEGOINT_COMMUNICATION_END, + PASSPORT_AUTH_LEGOINT_DISCONNECT, + PASSPORT_AUTH_WORLD_COMMUNICATION_START, + PASSPORT_AUTH_CLIENT_OS, + PASSPORT_AUTH_WORLD_PACKET_RECEIVED, + PASSPORT_AUTH_IM_COMMUNICATION_START, + PASSPORT_AUTH_IM_LOGIN_START, + PASSPORT_AUTH_IM_LOGIN_ALREADY_LOGGED_IN, + PASSPORT_AUTH_IM_OTHER_LOGIN_REMOVED, + PASSPORT_AUTH_IM_LOGIN_QUEUED, + PASSPORT_AUTH_IM_LOGIN_RESPONSE, + PASSPORT_AUTH_IM_COMMUNICATION_END, + PASSPORT_AUTH_WORLD_SESSION_CONFIRM_TO_AUTH, + PASSPORT_AUTH_WORLD_COMMUNICATION_FINISH, + PASSPORT_AUTH_WORLD_DISCONNECT, + NO_LEGO_INTERFACE, + DB_ERROR, + GM_REQUIRED, + NO_LEGO_WEBSERVICE_XML, + LEGO_WEBSERVICE_TIMEOUT, + LEGO_WEBSERVICE_ERROR, + NO_WORLD_SERVER +}; + +struct Stamp { + eStamps type; + uint32_t value; + uint64_t timestamp; + + Stamp(eStamps type, uint32_t value, uint64_t timestamp = time(nullptr)){ + this->type = type; + this->value = value; + this->timestamp = timestamp; + } + + void Serialize(RakNet::BitStream* outBitStream); +}; + +enum class ClientOS : uint8_t { + UNKNOWN, + WINDOWS, + MACOS +}; + +enum class LanguageCodeID : uint16_t { + de_DE = 0x0407, + en_US = 0x0409, + en_GB = 0x0809 +}; + +template <> +struct magic_enum::customize::enum_range { + static constexpr int min = 1031; + static constexpr int max = 2057; +}; + +enum class Language : uint32_t { + en_US, + pl_US, + de_DE, + en_GB, +}; + namespace AuthPackets { void HandleHandshake(dServer* server, Packet* packet); void SendHandshake(dServer* server, const SystemAddress& sysAddr, const std::string& nextServerIP, uint16_t nextServerPort, const ServerType 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); + 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(); } diff --git a/dNet/dServer.h b/dNet/dServer.h index b0a3e11d5..ef47eea44 100644 --- a/dNet/dServer.h +++ b/dNet/dServer.h @@ -16,6 +16,14 @@ enum class ServerType : uint32_t { World }; +enum class ServiceId : uint32_t{ + General = 0, + Auth = 1, + Chat = 2, + World = 4, + Client = 5, +}; + namespace Game { using signal_t = volatile std::sig_atomic_t; }