mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-13 10:31:29 +00:00
777 lines
23 KiB
C++
777 lines
23 KiB
C++
#include "world_server.h"
|
|
#include "login_server.h"
|
|
#include "login_types.h"
|
|
#include "../common/ip_util.h"
|
|
#include "../common/strings.h"
|
|
#include "../common/repositories/login_world_servers_repository.h"
|
|
#include "../common/repositories/login_server_admins_repository.h"
|
|
|
|
extern LoginServer server;
|
|
extern Database database;
|
|
|
|
WorldServer::WorldServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> worldserver_connection)
|
|
{
|
|
m_connection = worldserver_connection;
|
|
m_zones_booted = 0;
|
|
m_players_online = 0;
|
|
m_server_status = 0;
|
|
m_server_id = 0;
|
|
m_server_list_type_id = 0;
|
|
m_server_process_type = 0;
|
|
m_is_server_authorized_to_list = false;
|
|
m_is_server_trusted = false;
|
|
m_is_server_logged_in = false;
|
|
|
|
worldserver_connection->OnMessage(
|
|
ServerOP_NewLSInfo,
|
|
std::bind(&WorldServer::ProcessNewLSInfo, this, std::placeholders::_1, std::placeholders::_2)
|
|
);
|
|
|
|
worldserver_connection->OnMessage(
|
|
ServerOP_LSStatus,
|
|
std::bind(&WorldServer::ProcessLSStatus, this, std::placeholders::_1, std::placeholders::_2)
|
|
);
|
|
|
|
worldserver_connection->OnMessage(
|
|
ServerOP_UsertoWorldRespLeg,
|
|
std::bind(
|
|
&WorldServer::ProcessUserToWorldResponseLegacy,
|
|
this,
|
|
std::placeholders::_1,
|
|
std::placeholders::_2
|
|
)
|
|
);
|
|
|
|
worldserver_connection->OnMessage(
|
|
ServerOP_UsertoWorldResp,
|
|
std::bind(&WorldServer::ProcessUserToWorldResponse, this, std::placeholders::_1, std::placeholders::_2)
|
|
);
|
|
|
|
worldserver_connection->OnMessage(
|
|
ServerOP_LSAccountUpdate,
|
|
std::bind(&WorldServer::ProcessLSAccountUpdate, this, std::placeholders::_1, std::placeholders::_2)
|
|
);
|
|
}
|
|
|
|
WorldServer::~WorldServer() = default;
|
|
|
|
void WorldServer::Reset()
|
|
{
|
|
m_server_id = 0;
|
|
m_zones_booted = 0;
|
|
m_players_online = 0;
|
|
m_server_status = 0;
|
|
m_server_list_type_id = 0;
|
|
m_server_process_type = 0;
|
|
m_is_server_authorized_to_list = false;
|
|
m_is_server_logged_in = false;
|
|
}
|
|
|
|
void WorldServer::ProcessNewLSInfo(uint16_t opcode, const EQ::Net::Packet &packet)
|
|
{
|
|
LogNetcode(
|
|
"Application packet received from server [{:#04x}] [Size: {}]\n{}",
|
|
opcode, packet.Length(), packet.ToString()
|
|
);
|
|
|
|
if (packet.Length() < sizeof(LoginserverNewWorldRequest)) {
|
|
LogError(
|
|
"Received application packet with opcode ServerOP_NewLSInfo, but it was too small. Discarded to avoid buffer overrun."
|
|
);
|
|
return;
|
|
}
|
|
|
|
auto *r = (LoginserverNewWorldRequest *) packet.Data();
|
|
|
|
// If remote IP is missing, use local IP unless it's 127.0.0.1
|
|
if (r->remote_ip_address[0] == '\0' && r->local_ip_address[0] != '\0' &&
|
|
strcmp(r->local_ip_address, "127.0.0.1") != 0) {
|
|
strncpy(r->remote_ip_address, r->local_ip_address, sizeof(r->remote_ip_address) - 1);
|
|
r->remote_ip_address[sizeof(r->remote_ip_address) - 1] = '\0'; // Ensure null termination
|
|
}
|
|
|
|
LogInfo(
|
|
"New World Server Info | name [{}] shortname [{}] remote_address [{}] local_address [{}] account [{}] password [{}] server_type [{}]",
|
|
r->server_long_name,
|
|
r->server_short_name,
|
|
r->remote_ip_address,
|
|
r->local_ip_address,
|
|
r->account_name,
|
|
r->account_password,
|
|
r->server_process_type
|
|
);
|
|
|
|
HandleNewWorldserver(r);
|
|
}
|
|
|
|
void WorldServer::ProcessLSStatus(uint16_t opcode, const EQ::Net::Packet &packet)
|
|
{
|
|
LogNetcode(
|
|
"Application packet received from server [{:#04x}] [Size: {}]\n{}",
|
|
opcode,
|
|
packet.Length(),
|
|
packet.ToString()
|
|
);
|
|
|
|
if (packet.Length() < sizeof(LoginserverWorldStatusUpdate)) {
|
|
LogError(
|
|
"Received application packet from server that had opcode ServerOP_LSStatus, but was too small. Discarded to avoid buffer overrun"
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
auto *ls_status = (LoginserverWorldStatusUpdate *) packet.Data();
|
|
|
|
LogDebug(
|
|
"World Server Status Update Received | Server [{}] Status [{}] Players [{}] Zones [{}]",
|
|
m_server_long_name,
|
|
ls_status->status,
|
|
ls_status->num_players,
|
|
ls_status->num_zones
|
|
);
|
|
|
|
HandleWorldserverStatusUpdate(ls_status);
|
|
}
|
|
|
|
void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Net::Packet &packet)
|
|
{
|
|
LogNetcode(
|
|
"Application packet received from server [{:#04x}] [Size: {}]\n{}",
|
|
opcode,
|
|
packet.Length(),
|
|
packet.ToString()
|
|
);
|
|
|
|
if (packet.Length() < sizeof(UsertoWorldResponseLegacy)) {
|
|
LogError(
|
|
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
|
"but was too small. Discarded to avoid buffer overrun"
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
auto *res = (UsertoWorldResponseLegacy *) packet.Data();
|
|
|
|
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
|
std::string db_loginserver = "local";
|
|
if (std::getenv("LSPX")) {
|
|
db_loginserver = "eqemu";
|
|
}
|
|
Client *c = server.client_manager->GetClient(res->lsaccountid, db_loginserver);
|
|
if (c) {
|
|
LogDebug(
|
|
"Found client with user id of [{}] and account name of [{}]",
|
|
res->lsaccountid,
|
|
c->GetAccountName()
|
|
);
|
|
|
|
auto *outapp = new EQApplicationPacket(
|
|
OP_PlayEverquestResponse,
|
|
sizeof(PlayEverquestResponse)
|
|
);
|
|
|
|
auto *play = (PlayEverquestResponse *) outapp->pBuffer;
|
|
play->base_header.sequence = c->GetCurrentPlaySequence();
|
|
play->server_number = c->GetSelectedPlayServerID();
|
|
|
|
if (res->response > 0) {
|
|
play->base_reply.success = true;
|
|
SendClientAuthToWorld(c);
|
|
}
|
|
|
|
switch (res->response) {
|
|
case UserToWorldStatusSuccess:
|
|
play->base_reply.error_str_id = LS::ErrStr::ERROR_NONE;
|
|
break;
|
|
case UserToWorldStatusWorldUnavail:
|
|
play->base_reply.error_str_id = LS::ErrStr::ERROR_SERVER_UNAVAILABLE;
|
|
break;
|
|
case UserToWorldStatusSuspended:
|
|
play->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_SUSPENDED;
|
|
break;
|
|
case UserToWorldStatusBanned:
|
|
play->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_BANNED;
|
|
break;
|
|
case UserToWorldStatusWorldAtCapacity:
|
|
play->base_reply.error_str_id = LS::ErrStr::ERROR_WORLD_MAX_CAPACITY;
|
|
break;
|
|
case UserToWorldStatusAlreadyOnline:
|
|
play->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER;
|
|
break;
|
|
default:
|
|
play->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
LogDebug(
|
|
"Sending play response: allowed [{}] sequence [{}] server number [{}] message [{}]",
|
|
play->base_reply.success,
|
|
play->base_header.sequence,
|
|
play->server_number,
|
|
play->base_reply.error_str_id
|
|
);
|
|
|
|
c->SendPlayResponse(outapp);
|
|
delete outapp;
|
|
}
|
|
else {
|
|
LogError(
|
|
"Received User-To-World Response for [{}] but could not find the client referenced!",
|
|
res->lsaccountid
|
|
);
|
|
}
|
|
}
|
|
|
|
void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Packet &packet)
|
|
{
|
|
LogNetcode(
|
|
"Application packet received from server [{:#04x}] [Size: {}]\n{}",
|
|
opcode,
|
|
packet.Length(),
|
|
packet.ToString()
|
|
);
|
|
|
|
if (packet.Length() < sizeof(UsertoWorldResponse)) {
|
|
LogError(
|
|
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
|
"but was too small. Discarded to avoid buffer overrun"
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
auto res = (UsertoWorldResponse *) packet.Data();
|
|
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
|
|
|
Client *c = server.client_manager->GetClient(
|
|
res->lsaccountid,
|
|
res->login
|
|
);
|
|
|
|
if (c) {
|
|
LogDebug(
|
|
"Found client with user id of [{}] and account name of {}",
|
|
res->lsaccountid,
|
|
c->GetAccountName().c_str()
|
|
);
|
|
|
|
auto *outapp = new EQApplicationPacket(
|
|
OP_PlayEverquestResponse,
|
|
sizeof(PlayEverquestResponse)
|
|
);
|
|
|
|
auto *r = (PlayEverquestResponse *) outapp->pBuffer;
|
|
r->base_header.sequence = c->GetCurrentPlaySequence();
|
|
r->server_number = c->GetSelectedPlayServerID();
|
|
|
|
LogDebug(
|
|
"Found sequence and play of [{}] [{}]",
|
|
c->GetCurrentPlaySequence(),
|
|
c->GetSelectedPlayServerID()
|
|
);
|
|
|
|
LogDebug("[Size: [{}]] {}", outapp->size, DumpPacketToString(outapp));
|
|
|
|
if (res->response > 0) {
|
|
r->base_reply.success = true;
|
|
SendClientAuthToWorld(c);
|
|
}
|
|
|
|
switch (res->response) {
|
|
case UserToWorldStatusSuccess:
|
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_NONE;
|
|
break;
|
|
case UserToWorldStatusWorldUnavail:
|
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_SERVER_UNAVAILABLE;
|
|
break;
|
|
case UserToWorldStatusSuspended:
|
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_SUSPENDED;
|
|
break;
|
|
case UserToWorldStatusBanned:
|
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_BANNED;
|
|
break;
|
|
case UserToWorldStatusWorldAtCapacity:
|
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_WORLD_MAX_CAPACITY;
|
|
break;
|
|
case UserToWorldStatusAlreadyOnline:
|
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER;
|
|
break;
|
|
default:
|
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
LogDebug(
|
|
"Sending play response with following data, allowed [{}], sequence {}, server number {}, message {}",
|
|
r->base_reply.success,
|
|
r->base_header.sequence,
|
|
r->server_number,
|
|
r->base_reply.error_str_id
|
|
);
|
|
|
|
c->SendPlayResponse(outapp);
|
|
delete outapp;
|
|
}
|
|
else {
|
|
LogError(
|
|
"Received User-To-World Response for [{}] but could not find the client referenced!.",
|
|
res->lsaccountid
|
|
);
|
|
}
|
|
}
|
|
|
|
void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &packet)
|
|
{
|
|
LogNetcode(
|
|
"Application packet received from server [{:#04x}] [Size: {}]\n{}",
|
|
opcode,
|
|
packet.Length(),
|
|
packet.ToString()
|
|
);
|
|
|
|
if (packet.Length() < sizeof(LoginserverAccountUpdate)) {
|
|
LogError(
|
|
"Received application packet from server that had opcode ServerLSAccountUpdate_Struct, "
|
|
"but was too small. Discarded to avoid buffer overrun"
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
LogDebug("ServerOP_LSAccountUpdate packet received from [{}]", m_server_short_name);
|
|
|
|
auto *r = (LoginserverAccountUpdate *) packet.Data();
|
|
if (m_is_server_trusted) {
|
|
LogDebug("ServerOP_LSAccountUpdate update processed for: [{}]", r->user_account_name);
|
|
|
|
LoginAccountContext c{};
|
|
c.username = r->user_account_name;
|
|
c.source_loginserver = "local";
|
|
auto a = LoginAccountsRepository::GetAccountFromContext(database, c);
|
|
if (a.id > 0) {
|
|
a.account_email = r->user_email;
|
|
a.account_password = r->user_account_password;
|
|
a.last_ip_address = "0.0.0.0";
|
|
LoginAccountsRepository::UpdateOne(database, a);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldServer::HandleNewWorldserver(LoginserverNewWorldRequest *req)
|
|
{
|
|
if (m_is_server_logged_in) {
|
|
LogInfo("Login server was already marked as logged in, returning");
|
|
return;
|
|
}
|
|
|
|
if (!HandleNewWorldserverValidation(req)) {
|
|
LogError("failed validation rules");
|
|
return;
|
|
}
|
|
|
|
SanitizeWorldServerName(req->server_long_name);
|
|
|
|
m_server_long_name = req->server_long_name;
|
|
m_server_short_name = req->server_short_name;
|
|
m_account_password = req->account_password;
|
|
m_account_name = req->account_name;
|
|
m_local_ip = req->local_ip_address;
|
|
m_remote_ip_address = req->remote_ip_address;
|
|
m_server_version = req->server_version;
|
|
m_protocol = req->protocol_version;
|
|
m_server_process_type = req->server_process_type;
|
|
m_is_server_logged_in = true;
|
|
|
|
// Handle Duplicate Servers
|
|
if (server.server_manager->DoesServerExist(m_server_long_name, m_server_short_name, this)) {
|
|
if (server.options.IsRejectingDuplicateServers()) {
|
|
LogError("World tried to login but a server with that name already exists");
|
|
return;
|
|
}
|
|
LogInfo("World tried to login but a server with that name already exists, destroying [{}]", m_server_long_name);
|
|
server.server_manager->DestroyServerByName(m_server_long_name, m_server_short_name, this);
|
|
}
|
|
|
|
LoginWorldContext c;
|
|
c.long_name = m_server_long_name;
|
|
c.short_name = m_server_short_name;
|
|
|
|
LoginServerAdminsRepository::LoginServerAdmins admin;
|
|
|
|
// Handle Admin Authentication
|
|
if (!m_account_name.empty() && !m_account_password.empty()) {
|
|
admin = LoginServerAdminsRepository::GetByName(database, m_account_name);
|
|
|
|
LoginWorldAdminAccountContext ac;
|
|
ac.id = admin.id;
|
|
ac.username = m_account_name;
|
|
ac.password = m_account_password;
|
|
ac.password_hash = admin.account_password;
|
|
|
|
if (admin.id && WorldServer::ValidateWorldServerAdminLogin(ac, admin)) {
|
|
LogDebug(
|
|
"Authenticated world admin [{}] ({}) for world [{}]",
|
|
m_account_name,
|
|
admin.id,
|
|
m_server_short_name
|
|
);
|
|
c.admin_id = admin.id;
|
|
m_is_server_authorized_to_list = true;
|
|
}
|
|
}
|
|
|
|
auto world = LoginWorldServersRepository::GetFromWorldContext(database, c);
|
|
if (!world.id) {
|
|
if (!server.options.IsUnregisteredAllowed()) {
|
|
LogError("WorldServer [{}] is not registered, and unregistered servers are not allowed",
|
|
m_server_long_name);
|
|
return;
|
|
}
|
|
|
|
LogInfo("Server [{}] is not registered, handling as unregistered", m_server_long_name);
|
|
m_is_server_authorized_to_list = true;
|
|
|
|
auto w = LoginWorldServersRepository::NewEntity();
|
|
w.long_name = m_server_long_name;
|
|
w.short_name = m_server_short_name;
|
|
w.last_ip_address = m_remote_ip_address;
|
|
w.login_server_list_type_id = LS::ServerType::Standard;
|
|
w.last_login_date = std::time(nullptr);
|
|
auto created = LoginWorldServersRepository::InsertOne(database, w);
|
|
if (!created.id) {
|
|
LogError("Failed to auto-register world server [{}]", m_server_long_name);
|
|
return;
|
|
}
|
|
|
|
LogInfo(
|
|
"Auto-registered world server [{}] with ID [{}]",
|
|
m_server_long_name,
|
|
created.id
|
|
);
|
|
}
|
|
else {
|
|
m_server_description = world.tag_description;
|
|
m_server_id = world.id;
|
|
m_is_server_trusted = world.is_server_trusted;
|
|
m_server_list_type_id = world.login_server_list_type_id;
|
|
m_is_server_authorized_to_list = true;
|
|
|
|
LogInfo(
|
|
"Server ID [{}] long_name [{}] short_name [{}] successfully authenticated",
|
|
world.id,
|
|
world.long_name,
|
|
world.short_name
|
|
);
|
|
}
|
|
|
|
LogInfo(
|
|
"World registration id [{}] for server [{}] ip_address [{}]",
|
|
m_server_id,
|
|
m_server_long_name,
|
|
m_remote_ip_address
|
|
);
|
|
|
|
// Update the last login date and IP address
|
|
world.last_login_date = std::time(nullptr);
|
|
world.last_ip_address = m_remote_ip_address;
|
|
LoginWorldServersRepository::UpdateOne(database, world);
|
|
|
|
WorldServer::FormatWorldServerName(
|
|
req->server_long_name,
|
|
m_server_list_type_id
|
|
);
|
|
|
|
m_server_long_name = req->server_long_name;
|
|
}
|
|
|
|
void WorldServer::HandleWorldserverStatusUpdate(LoginserverWorldStatusUpdate *u)
|
|
{
|
|
m_players_online = u->num_players;
|
|
m_zones_booted = u->num_zones;
|
|
m_server_status = u->status;
|
|
}
|
|
|
|
void WorldServer::SendClientAuthToWorld(Client *c)
|
|
{
|
|
EQ::Net::DynamicPacket outapp;
|
|
ClientAuth a{};
|
|
|
|
a.loginserver_account_id = c->GetAccountID();
|
|
|
|
strncpy(a.account_name, c->GetAccountName().c_str(), 30);
|
|
strncpy(a.key, c->GetLoginKey().c_str(), 30);
|
|
|
|
a.lsadmin = 0;
|
|
a.is_world_admin = 0;
|
|
a.ip_address = inet_addr(c->GetConnection()->GetRemoteAddr().c_str());
|
|
strncpy(a.loginserver_name, &c->GetLoginServerName()[0], 64);
|
|
|
|
const std::string &client_address(c->GetConnection()->GetRemoteAddr());
|
|
std::string world_address(m_connection->Handle()->RemoteIP());
|
|
|
|
if (client_address == world_address) {
|
|
a.is_client_from_local_network = 1;
|
|
}
|
|
else if (IpUtil::IsIpInPrivateRfc1918(client_address)) {
|
|
LogInfo("Client is authenticating from a local address [{}]", client_address);
|
|
a.is_client_from_local_network = 1;
|
|
}
|
|
else {
|
|
a.is_client_from_local_network = 0;
|
|
}
|
|
|
|
struct in_addr ip_addr{};
|
|
ip_addr.s_addr = a.ip_address;
|
|
|
|
LogInfo(
|
|
"Client authentication response: world_address [{}] client_address [{}]",
|
|
world_address,
|
|
client_address
|
|
);
|
|
|
|
LogInfo(
|
|
"Sending Client Authentication Response ls_account_id [{}] ls_name [{}] name [{}] key [{}] ls_admin [{}] "
|
|
"world_admin [{}] ip [{}] local [{}]",
|
|
a.loginserver_account_id,
|
|
a.loginserver_name,
|
|
a.account_name,
|
|
a.key,
|
|
a.lsadmin,
|
|
a.is_world_admin,
|
|
inet_ntoa(ip_addr),
|
|
a.is_client_from_local_network
|
|
);
|
|
|
|
outapp.PutSerialize(0, a);
|
|
m_connection->Send(ServerOP_LSClientAuth, outapp);
|
|
|
|
LogNetcode(
|
|
"Sending [{:#04x}] [Size: {}]\n{}",
|
|
ServerOP_LSClientAuth,
|
|
outapp.Length(),
|
|
outapp.ToString()
|
|
);
|
|
}
|
|
|
|
constexpr static int MAX_ACCOUNT_NAME_LENGTH = 30;
|
|
constexpr static int MAX_ACCOUNT_PASSWORD_LENGTH = 30;
|
|
constexpr static int MAX_SERVER_LONG_NAME_LENGTH = 200;
|
|
constexpr static int MAX_SERVER_SHORT_NAME_LENGTH = 50;
|
|
constexpr static int MAX_SERVER_LOCAL_ADDRESS_LENGTH = 125;
|
|
constexpr static int MAX_SERVER_REMOTE_ADDRESS_LENGTH = 125;
|
|
constexpr static int MAX_SERVER_VERSION_LENGTH = 64;
|
|
constexpr static int MAX_SERVER_PROTOCOL_VERSION = 25;
|
|
|
|
bool WorldServer::HandleNewWorldserverValidation(LoginserverNewWorldRequest *r)
|
|
{
|
|
if (strlen(r->account_name) >= MAX_ACCOUNT_NAME_LENGTH) {
|
|
LogError("HandleNewWorldserver error [account_name] was too long | max [{}]", MAX_ACCOUNT_NAME_LENGTH);
|
|
return false;
|
|
}
|
|
else if (strlen(r->account_password) >= MAX_ACCOUNT_PASSWORD_LENGTH) {
|
|
LogError("HandleNewWorldserver error [account_password] was too long | max [{}]", MAX_ACCOUNT_PASSWORD_LENGTH);
|
|
return false;
|
|
}
|
|
else if (strlen(r->server_long_name) >= MAX_SERVER_LONG_NAME_LENGTH) {
|
|
LogError("HandleNewWorldserver error [server_long_name] was too long | max [{}]", MAX_SERVER_LONG_NAME_LENGTH);
|
|
return false;
|
|
}
|
|
else if (strlen(r->server_short_name) >= MAX_SERVER_SHORT_NAME_LENGTH) {
|
|
LogError("HandleNewWorldserver error [server_short_name] was too long | max [{}]",
|
|
MAX_SERVER_SHORT_NAME_LENGTH);
|
|
return false;
|
|
}
|
|
else if (strlen(r->server_version) >= MAX_SERVER_VERSION_LENGTH) {
|
|
LogError("HandleNewWorldserver error [server_version] was too long | max [{}]", MAX_SERVER_VERSION_LENGTH);
|
|
return false;
|
|
}
|
|
else if (strlen(r->protocol_version) >= MAX_SERVER_PROTOCOL_VERSION) {
|
|
LogError("HandleNewWorldserver error [protocol_version] was too long | max [{}]", MAX_SERVER_PROTOCOL_VERSION);
|
|
return false;
|
|
}
|
|
|
|
if (strlen(r->local_ip_address) <= MAX_SERVER_LOCAL_ADDRESS_LENGTH) {
|
|
if (strlen(r->local_ip_address) == 0) {
|
|
LogError("HandleNewWorldserver error, local address was null, defaulting to localhost");
|
|
m_local_ip = "127.0.0.1";
|
|
}
|
|
else {
|
|
m_local_ip = r->local_ip_address;
|
|
}
|
|
}
|
|
else {
|
|
LogError("HandleNewWorldserver error, local address was too long | max [{}]", MAX_SERVER_LOCAL_ADDRESS_LENGTH);
|
|
return false;
|
|
}
|
|
|
|
if (strlen(r->remote_ip_address) <= MAX_SERVER_REMOTE_ADDRESS_LENGTH) {
|
|
if (strlen(r->remote_ip_address) == 0) {
|
|
m_remote_ip_address = GetConnection()->Handle()->RemoteIP();
|
|
|
|
LogWarning(
|
|
"Remote address was null, defaulting to stream address [{}]",
|
|
m_remote_ip_address
|
|
);
|
|
}
|
|
else {
|
|
m_remote_ip_address = r->remote_ip_address;
|
|
}
|
|
}
|
|
else {
|
|
m_remote_ip_address = GetConnection()->Handle()->RemoteIP();
|
|
|
|
LogWarning(
|
|
"HandleNewWorldserver remote address was too long, defaulting to stream address [{}]",
|
|
m_remote_ip_address
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WorldServer::ValidateWorldServerAdminLogin(
|
|
LoginWorldAdminAccountContext &c,
|
|
LoginServerAdminsRepository::LoginServerAdmins &admin
|
|
)
|
|
{
|
|
auto encryption_mode = server.options.GetEncryptionMode();
|
|
if (eqcrypt_verify_hash(c.username, c.password, c.password_hash, encryption_mode)) {
|
|
return true;
|
|
}
|
|
|
|
if (encryption_mode < EncryptionModeArgon2) {
|
|
encryption_mode = EncryptionModeArgon2;
|
|
}
|
|
|
|
uint32 insecure_source_encryption_mode = 0;
|
|
auto verify_encryption = [&](int start, int end) {
|
|
for (int i = start; i <= end; ++i) {
|
|
if (i != encryption_mode && eqcrypt_verify_hash(c.username, c.password, c.password_hash, i)) {
|
|
LogDebug("Checking for [{}] world admin", GetEncryptionByModeId(i));
|
|
insecure_source_encryption_mode = i;
|
|
}
|
|
}
|
|
};
|
|
|
|
switch (c.password_hash.length()) {
|
|
case CryptoHash::md5_hash_length:
|
|
verify_encryption(EncryptionModeMD5, EncryptionModeMD5Triple);
|
|
break;
|
|
case CryptoHash::sha1_hash_length:
|
|
verify_encryption(EncryptionModeSHA, EncryptionModeSHATriple);
|
|
break;
|
|
case CryptoHash::sha512_hash_length:
|
|
verify_encryption(EncryptionModeSHA512, EncryptionModeSHA512Triple);
|
|
break;
|
|
}
|
|
|
|
if (insecure_source_encryption_mode > 0) {
|
|
LogInfo(
|
|
"Updated insecure world_admin_username [{}] from mode [{}] ({}) to mode [{}] ({})",
|
|
c.username,
|
|
GetEncryptionByModeId(insecure_source_encryption_mode),
|
|
insecure_source_encryption_mode,
|
|
GetEncryptionByModeId(encryption_mode),
|
|
encryption_mode
|
|
);
|
|
|
|
admin.account_password = eqcrypt_hash(c.username, c.password, encryption_mode);
|
|
LoginServerAdminsRepository::UpdateOne(database, admin);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_local_ip, LSClientVersion version) const
|
|
{
|
|
// see LoginClientServerData_Struct
|
|
if (use_local_ip) {
|
|
out.WriteString(GetLocalIP());
|
|
}
|
|
else {
|
|
out.WriteString(m_remote_ip_address);
|
|
}
|
|
|
|
if (version == cv_larion) {
|
|
out.WriteUInt32(9000);
|
|
}
|
|
|
|
switch (GetServerListID()) {
|
|
case LS::ServerType::Legends:
|
|
out.WriteInt32(LS::ServerTypeFlags::Legends);
|
|
break;
|
|
case LS::ServerType::Preferred:
|
|
out.WriteInt32(LS::ServerTypeFlags::Preferred);
|
|
break;
|
|
default:
|
|
out.WriteInt32(LS::ServerTypeFlags::Standard);
|
|
break;
|
|
}
|
|
if (version == cv_larion) {
|
|
auto server_id = m_server_id;
|
|
//if this is 0, the client will not show the server in the list
|
|
out.WriteUInt32(1);
|
|
out.WriteUInt32(server_id);
|
|
}
|
|
else {
|
|
out.WriteUInt32(m_server_id);
|
|
}
|
|
|
|
out.WriteString(m_server_long_name);
|
|
out.WriteString("us"); // country code
|
|
out.WriteString("en"); // language code
|
|
|
|
// 0 = Up, 1 = Down, 2 = Up, 3 = down, 4 = locked, 5 = locked(down)
|
|
if (GetStatus() < 0) {
|
|
if (GetZonesBooted() == 0) {
|
|
out.WriteInt32(LS::ServerStatusFlags::Down);
|
|
}
|
|
else {
|
|
out.WriteInt32(LS::ServerStatusFlags::Locked);
|
|
}
|
|
}
|
|
else {
|
|
out.WriteInt32(LS::ServerStatusFlags::Up);
|
|
}
|
|
|
|
out.WriteUInt32(GetPlayersOnline());
|
|
}
|
|
|
|
void WorldServer::FormatWorldServerName(char *name, int8 server_list_type)
|
|
{
|
|
std::string server_long_name = name;
|
|
server_long_name = Strings::Trim(server_long_name);
|
|
|
|
bool name_set_to_bottom = false;
|
|
if (server_list_type == LS::ServerType::Standard) {
|
|
if (server.options.IsWorldDevTestServersListBottom()) {
|
|
std::string s = Strings::ToLower(server_long_name);
|
|
if (s.find("dev") != std::string::npos) {
|
|
server_long_name = fmt::format("|D| {}", server_long_name);
|
|
name_set_to_bottom = true;
|
|
}
|
|
else if (s.find("test") != std::string::npos) {
|
|
server_long_name = fmt::format("|T| {}", server_long_name);
|
|
name_set_to_bottom = true;
|
|
}
|
|
else if (s.find("installer") != std::string::npos) {
|
|
server_long_name = fmt::format("|I| {}", server_long_name);
|
|
name_set_to_bottom = true;
|
|
}
|
|
}
|
|
if (server.options.IsWorldSpecialCharacterStartListBottom() && !name_set_to_bottom) {
|
|
auto first_char = server_long_name.c_str()[0];
|
|
if (IsAllowedWorldServerCharacterList(first_char) && first_char != '|') {
|
|
server_long_name = fmt::format("|*| {}", server_long_name);
|
|
name_set_to_bottom = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
strn0cpy(name, server_long_name.c_str(), 201);
|
|
}
|