mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
Nothing in the server writes or reads a packet by hand any more, so the helpers for doing so go: - CBITSTREAM, CMSGHEADER, CINSTREAM, CINSTREAM_SKIP_HEADER, SEND_PACKET, SEND_PACKET_BROADCAST and HEADER_SIZE leave dCommonVars.h; - the free BitStreamUtils::WriteHeader (LUBitStream::WriteHeader writes the same bytes) and the unused PacketUtils::SavePacket are deleted. The last raw reads are replaced: WorldServer builds its input stream directly, the master packet logs read the header with LUBitStream::ReadHeader instead of peeking at packet->data[1] and [3], and MessageInspector reads a sent game message's header with the new NetGameMsg::ReadPacketHeader (the counterpart of WritePacket) instead of memcmp/memcpy. packet->data[0] is still compared with RakNet's own connection IDs. The frozen oracles keep using the macros verbatim through the test-only tests/dGameTests/LegacyPacketMacros.h; the HeaderSkip tests, which only tested CINSTREAM_SKIP_HEADER, are removed. docs/PacketArchitecture.md: "where we are" now describes the final state and what still touches raw bytes (RakNet IDs, replica headers, behavior bit streams), and a new section collects the known wire discrepancies found during the conversion, with client addresses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
341 lines
14 KiB
C++
341 lines
14 KiB
C++
#ifndef COMMONAUTHPACKETSLEGACY_H
|
|
#define COMMONAUTHPACKETSLEGACY_H
|
|
|
|
// FROZEN ORACLE - DO NOT EDIT.
|
|
// Verbatim copies of the hand written functions that CommonPackets / AuthPackets::LoginRequest /
|
|
// ClientPackets::LoginResponse replaced (dNet/AuthPackets.cpp and dNet/dServer.cpp before the conversion).
|
|
// Only the namespace changed (and calls to its own functions name the new namespace).
|
|
// The byte-equality tests run the same inputs through these and through the new structs and require
|
|
// identical bytes, so the wire format is pinned even after the production code is deleted.
|
|
|
|
#include "LegacyPacketMacros.h"
|
|
#include "AuthPackets.h"
|
|
#include "BitStreamUtils.h"
|
|
#include "ClientPackets.h"
|
|
#include "Database.h"
|
|
#include "dClient/ClientVersion.h"
|
|
#include "dCommonVars.h"
|
|
#include "dConfig.h"
|
|
#include "dServer.h"
|
|
#include "eGameMasterLevel.h"
|
|
#include "eLoginResponse.h"
|
|
#include "eServerDisconnectIdentifiers.h"
|
|
#include "Game.h"
|
|
#include "GeneralUtils.h"
|
|
#include "Logger.h"
|
|
#include "MD5.h"
|
|
#include "MessageType/Client.h"
|
|
#include "MessageType/Master.h"
|
|
#include "MessageType/Server.h"
|
|
#include "ServiceType.h"
|
|
#include "StringifiedEnum.h"
|
|
#include "ZoneInstanceManager.h"
|
|
|
|
#include <bcrypt/BCrypt.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <ctime>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace LegacyAuthPackets {
|
|
inline std::vector<uint32_t> claimCodes;
|
|
|
|
inline 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;
|
|
}
|
|
|
|
inline void 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);
|
|
}
|
|
|
|
inline void 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());
|
|
}
|
|
|
|
inline 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) {
|
|
stamps.emplace_back(eStamps::PASSPORT_AUTH_IM_LOGIN_START, 1);
|
|
RakNet::BitStream loginResponse;
|
|
BitStreamUtils::WriteHeader(loginResponse, ServiceType::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<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 session key for user %s", username.c_str());
|
|
}
|
|
}
|
|
|
|
inline void 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);
|
|
LegacyAuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::INVALID_USER, "", "", 2001, username, stamps);
|
|
return;
|
|
}
|
|
|
|
// The password first: someone who doesn't know it learns nothing about the account (ban details, lock, play key),
|
|
// and a failed attempt changes nothing (an expired ban is only lifted for the real owner)
|
|
if (::bcrypt_checkpw(password.GetAsString().c_str(), accountInfo->bcryptPassword.c_str()) != 0) {
|
|
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
|
|
LegacyAuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::WRONG_PASS, "", "", 2001, username, stamps);
|
|
LOG("Wrong password used");
|
|
return;
|
|
}
|
|
|
|
//If we aren't running in live mode, then only GMs are allowed to enter:
|
|
if (Game::config->GetValue<bool>("closed_to_non_devs", false) && accountInfo->maxGmLevel == eGameMasterLevel::CIVILIAN) {
|
|
stamps.emplace_back(eStamps::GM_REQUIRED, 1);
|
|
LegacyAuthPackets::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);
|
|
LegacyAuthPackets::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);
|
|
LegacyAuthPackets::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);
|
|
LegacyAuthPackets::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);
|
|
}
|
|
|
|
// A temporary ban that has run out is lifted as the player logs in
|
|
if (accountInfo->banned && accountInfo->banExpires > 0 && accountInfo->banExpires <= static_cast<int64_t>(std::time(nullptr))) {
|
|
Database::Get()->SetAccountBan(accountInfo->id, false, 0, "");
|
|
Database::Get()->InsertAccountNote({ 0, accountInfo->id, "unban", "Temporary ban ended", "[server]", static_cast<int64_t>(std::time(nullptr)) });
|
|
accountInfo->banned = false;
|
|
LOG("Temporary ban of %s ended", username.c_str());
|
|
}
|
|
|
|
if (accountInfo->banned) {
|
|
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
|
|
std::string message;
|
|
if (accountInfo->banExpires > 0) {
|
|
char until[32];
|
|
const std::time_t expires = accountInfo->banExpires;
|
|
std::strftime(until, sizeof(until), "%Y-%m-%d %H:%M UTC", std::gmtime(&expires));
|
|
message = std::string("You are banned until ") + until + ".";
|
|
}
|
|
if (!accountInfo->banReason.empty()) message += (message.empty() ? "" : " ") + std::string("Reason: ") + accountInfo->banReason;
|
|
LegacyAuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::BANNED, message, "", 2001, username, stamps);
|
|
return;
|
|
}
|
|
|
|
if (accountInfo->locked) {
|
|
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
|
|
LegacyAuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::ACCOUNT_LOCKED, "", "", 2001, username, stamps);
|
|
return;
|
|
}
|
|
|
|
{
|
|
SystemAddress system = packet->systemAddress; //Copy the sysAddr before the Packet gets destroyed from main
|
|
|
|
// Where accounts log in from, so staff can see accounts that share a connection (log_login_addresses, on by default)
|
|
if (Game::config->GetValue("log_login_addresses") != "0") {
|
|
Database::Get()->RecordLoginAddress(accountInfo->id, system.ToString(false), static_cast<int64_t>(std::time(nullptr)));
|
|
}
|
|
|
|
if (!server->GetIsConnectedToMaster()) {
|
|
stamps.emplace_back(eStamps::PASSPORT_AUTH_WORLD_DISCONNECT, 1);
|
|
LegacyAuthPackets::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 {
|
|
LegacyAuthPackets::SendLoginResponse(server, system, eLoginResponse::SUCCESS, "", zoneIP, zonePort, username, stamps);
|
|
});
|
|
}
|
|
|
|
for(auto const code: claimCodes){
|
|
Database::Get()->InsertRewardCode(accountInfo->id, code);
|
|
}
|
|
}
|
|
|
|
// dServer::Disconnect, the part that built the packet (it then sent it with mPeer->Send and closed the connection).
|
|
inline void WriteDisconnectNotify(RakNet::BitStream& bitStream, eServerDisconnectIdentifiers disconNotifyID) {
|
|
BitStreamUtils::WriteHeader(bitStream, ServiceType::COMMON, MessageType::Server::DISCONNECT_NOTIFY);
|
|
bitStream.Write(disconNotifyID);
|
|
}
|
|
}
|
|
|
|
#endif // COMMONAUTHPACKETSLEGACY_H
|