#include "AuthPackets.h" #include "BitStreamUtils.h" #include "dNetCommon.h" #include "dServer.h" #include "Logger.h" #include "Database.h" #include "ZoneInstanceManager.h" #include "MD5.h" #include "GeneralUtils.h" #include "dClient/ClientVersion.h" #include #include "BitStream.h" #include #include "Game.h" #include "dConfig.h" #include "eServerDisconnectIdentifiers.h" #include "eLoginResponse.h" #include "eConnectionType.h" #include "MessageType/Server.h" #include "MessageType/Master.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"); 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()); } } void AuthPackets::HandleHandshake(dServer* server, Packet* packet) { 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); SendHandshake(server, packet->systemAddress, server->GetIP(), server->GetPort(), server->GetServerType()); } void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, const std::string& nextServerIP, uint16_t nextServerPort, const ServerType serverType) { RakNet::BitStream bitStream; BitStreamUtils::WriteHeader(bitStream, eConnectionType::SERVER, 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); if (serverType == ServerType::Auth) bitStream.Write(ServiceId::Auth); else if (serverType == ServerType::World) bitStream.Write(ServiceId::World); else bitStream.Write(ServiceId::General); bitStream.Write(219818241584); 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; } 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"); } 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); } } 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, MessageType::Client::LOGIN_RESPONSE); loginResponse.Write(responseCode); // 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, eConnectionType::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()); } }