[Bug Fix] Fix IP Exemptions. (#2189)

* [Bug Fix] Fix IP Exemptions.
- IP Exemptions were broken due to GetAccountID() returning 0 in logic somehow, resolved this by setting a variable to GetAccountID() at the beginning of the method.
- Fixed weird IP messages where the long form of IP was displayed instead of the string form.
- Fixes edge case where IP rule may be set to -1 and this will make anyone get instantly kicked if IP Exemptions were enabled as their IP Count would always be greater than -1.

* Update client.cpp

* Update client.cpp

* Update clientlist.cpp

* Update clientlist.cpp
This commit is contained in:
Kinglykrab 2022-05-27 23:57:55 -04:00 committed by GitHub
parent aaaee6c6a4
commit 7de50d0e60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 267 additions and 226 deletions

View File

@ -1469,41 +1469,25 @@ uint8 Database::GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16
return base_cap;
}
uint32 Database::GetCharacterInfo(
const char *iName,
uint32 *oAccID,
uint32 *oZoneID,
uint32 *oInstanceID,
float *oX,
float *oY,
float *oZ
)
uint32 Database::GetCharacterInfo(std::string character_name, uint32 *account_id, uint32 *zone_id, uint32 *instance_id)
{
std::string query = StringFormat(
"SELECT `id`, `account_id`, `zone_id`, `zone_instance`, `x`, `y`, `z` FROM `character_data` WHERE `name` = '%s'",
EscapeString(iName).c_str()
auto query = fmt::format(
"SELECT `id`, `account_id`, `zone_id`, `zone_instance` FROM `character_data` WHERE `name` = '{}'",
EscapeString(character_name)
);
auto results = QueryDatabase(query);
if (!results.Success()) {
if (!results.Success() || !results.RowCount()) {
return 0;
}
if (results.RowCount() != 1) {
return 0;
}
auto row = results.begin();
auto character_id = std::stoul(row[0]);
*account_id = std::stoul(row[1]);
*zone_id = std::stoul(row[2]);
*instance_id = std::stoul(row[3]);
auto row = results.begin();
uint32 charid = atoi(row[0]);
if (oAccID) { *oAccID = atoi(row[1]); }
if (oZoneID) { *oZoneID = atoi(row[2]); }
if (oInstanceID) { *oInstanceID = atoi(row[3]); }
if (oX) { *oX = atof(row[4]); }
if (oY) { *oY = atof(row[5]); }
if (oZ) { *oZ = atof(row[6]); }
return charid;
return character_id;
}
bool Database::UpdateLiveChar(char* charname, uint32 account_id) {
@ -1627,29 +1611,36 @@ uint32 Database::GetGroupID(const char* name){
return atoi(row[0]);
}
/* Is this really getting used properly... A half implementation ? Akkadius */
char* Database::GetGroupLeaderForLogin(const char* name, char* leaderbuf) {
strcpy(leaderbuf, "");
std::string Database::GetGroupLeaderForLogin(std::string character_name) {
uint32 group_id = 0;
std::string query = StringFormat("SELECT `groupid` FROM `group_id` WHERE `name` = '%s'", name);
auto query = fmt::format(
"SELECT `groupid` FROM `group_id` WHERE `name` = '{}'",
character_name
);
auto results = QueryDatabase(query);
for (auto row = results.begin(); row != results.end(); ++row)
if (row[0])
group_id = atoi(row[0]);
if (results.Success() && results.RowCount()) {
auto row = results.begin();
group_id = std::stoul(row[0]);
}
if (group_id == 0)
return leaderbuf;
if (!group_id) {
return std::string();
}
query = StringFormat("SELECT `leadername` FROM `group_leaders` WHERE `gid` = '%u' LIMIT 1", group_id);
query = fmt::format(
"SELECT `leadername` FROM `group_leaders` WHERE `gid` = {} LIMIT 1",
group_id
);
results = QueryDatabase(query);
for (auto row = results.begin(); row != results.end(); ++row)
if (row[0])
strcpy(leaderbuf, row[0]);
if (results.Success() && results.RowCount()) {
auto row = results.begin();
return row[0];
}
return leaderbuf;
return std::string();
}
void Database::SetGroupLeaderName(uint32 gid, const char* name) {
@ -2244,28 +2235,32 @@ bool Database::SaveTime(int8 minute, int8 hour, int8 day, int8 month, int16 year
}
int Database::GetIPExemption(std::string account_ip) {
std::string query = StringFormat("SELECT `exemption_amount` FROM `ip_exemptions` WHERE `exemption_ip` = '%s'", account_ip.c_str());
auto results = QueryDatabase(query);
if (results.Success() && results.RowCount() > 0) {
auto row = results.begin();
return atoi(row[0]);
}
return RuleI(World, MaxClientsPerIP);
}
void Database::SetIPExemption(std::string account_ip, int exemption_amount) {
std::string query = fmt::format(
"SELECT `exemption_id` FROM `ip_exemptions` WHERE `exemption_ip` = '{}'",
auto query = fmt::format(
"SELECT `exemption_amount` FROM `ip_exemptions` WHERE `exemption_ip` = '{}'",
account_ip
);
auto results = QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
return RuleI(World, MaxClientsPerIP);
}
auto row = results.begin();
return std::stoi(row[0]);
}
void Database::SetIPExemption(std::string account_ip, int exemption_amount) {
auto query = fmt::format(
"SELECT `exemption_id` FROM `ip_exemptions` WHERE `exemption_ip` = '{}'",
account_ip
);
uint32 exemption_id = 0;
if (results.Success() && results.RowCount() > 0) {
auto results = QueryDatabase(query);
if (results.Success() && results.RowCount()) {
auto row = results.begin();
exemption_id = atoi(row[0]);
exemption_id = std::stoul(row[0]);
}
query = fmt::format(
@ -2274,13 +2269,14 @@ void Database::SetIPExemption(std::string account_ip, int exemption_amount) {
exemption_amount
);
if (exemption_id != 0) {
if (exemption_id) {
query = fmt::format(
"UPDATE `ip_exemptions` SET `exemption_amount` = {} WHERE `exemption_ip` = '{}'",
exemption_amount,
account_ip
);
}
QueryDatabase(query);
}

View File

@ -131,7 +131,7 @@ public:
uint32 GetAccountIDByChar(uint32 char_id);
uint32 GetAccountIDByName(std::string account_name, std::string loginserver, int16* status = 0, uint32* lsid = 0);
uint32 GetCharacterID(const char *name);
uint32 GetCharacterInfo(const char* iName, uint32* oAccID = 0, uint32* oZoneID = 0, uint32* oInstanceID = 0, float* oX = 0, float* oY = 0, float* oZ = 0);
uint32 GetCharacterInfo(std::string character_name, uint32 *account_id, uint32 *zone_id, uint32 *instance_id);
uint32 GetGuildIDByCharID(uint32 char_id);
uint32 GetGroupIDByCharID(uint32 char_id);
uint32 GetRaidIDByCharID(uint32 char_id);
@ -207,8 +207,8 @@ public:
/* Groups */
char* GetGroupLeaderForLogin(const char* name,char* leaderbuf);
char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr);
std::string GetGroupLeaderForLogin(std::string character_name);
char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr);
uint32 GetGroupID(const char* name);

View File

@ -721,38 +721,40 @@ bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) {
}
bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
if (GetAccountID() == 0) {
LogInfo("Enter world with no logged in account");
auto account_id = GetAccountID();
if (!account_id) {
LogInfo("Enter world with no logged in account.");
eqs->Close();
return true;
}
if(GetAdmin() < 0)
{
LogInfo("Account banned or suspended");
if (GetAdmin() < 0) {
LogInfo("Account [{}] is banned or suspended.", account_id);
eqs->Close();
return true;
}
if (RuleB(World, EnableIPExemptions) || RuleI(World, MaxClientsPerIP) >= 0) {
if (
RuleB(World, EnableIPExemptions) ||
RuleI(World, MaxClientsPerIP) > 0
) {
client_list.GetCLEIP(GetIP()); //Check current CLE Entry IPs against incoming connection
}
EnterWorld_Struct *ew=(EnterWorld_Struct *)app->pBuffer;
strn0cpy(char_name, ew->name, 64);
auto ew = (EnterWorld_Struct *) app->pBuffer;
strn0cpy(char_name, ew->name, sizeof(char_name));
EQApplicationPacket *outapp;
uint32 tmpaccid = 0;
charid = database.GetCharacterInfo(char_name, &tmpaccid, &zone_id, &instance_id);
if (charid == 0) {
uint32 temporary_account_id = 0;
charid = database.GetCharacterInfo(char_name, &temporary_account_id, &zone_id, &instance_id);
if (!charid) {
LogInfo("Could not get CharInfo for [{}]", char_name);
eqs->Close();
return true;
}
// Make sure this account owns this character
if (tmpaccid != GetAccountID()) {
LogInfo("This account does not own the character named [{}]", char_name);
if (temporary_account_id != account_id) {
LogInfo("Account [{}] does not own the character named [{}] from account [{}]", account_id, char_name, temporary_account_id);
eqs->Close();
return true;
}
@ -761,25 +763,26 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
// (This is a literal translation of the original process..I don't see why it can't be changed to a single-target query over account iteration)
if (!is_player_zoning) {
size_t character_limit = EQ::constants::StaticLookup(eqs->ClientVersion())->CharacterCreationLimit;
if (character_limit > EQ::constants::CHARACTER_CREATION_LIMIT) { character_limit = EQ::constants::CHARACTER_CREATION_LIMIT; }
if (eqs->ClientVersion() == EQ::versions::ClientVersion::Titanium) { character_limit = Titanium::constants::CHARACTER_CREATION_LIMIT; }
if (character_limit > EQ::constants::CHARACTER_CREATION_LIMIT) {
character_limit = EQ::constants::CHARACTER_CREATION_LIMIT;
}
std::string tgh_query = StringFormat(
"SELECT "
"`id`, "
"name, "
"`level`, "
"last_login "
"FROM "
"character_data "
"WHERE `account_id` = %i ORDER BY `name` LIMIT %u", GetAccountID(), character_limit);
auto tgh_results = database.QueryDatabase(tgh_query);
if (eqs->ClientVersion() == EQ::versions::ClientVersion::Titanium) {
character_limit = Titanium::constants::CHARACTER_CREATION_LIMIT;
}
auto query = fmt::format(
"SELECT `id`, `name`, `level`, `last_login` FROM character_data WHERE `account_id` = {} ORDER BY `name` LIMIT {}",
account_id,
character_limit
);
auto results = database.QueryDatabase(query);
/* Check GoHome */
if (ew->return_home && !ew->tutorial) {
bool home_enabled = false;
for (auto row = tgh_results.begin(); row != tgh_results.end(); ++row) {
if (strcasecmp(row[1], char_name) == 0) {
for (auto row : results) {
if (!strcasecmp(row[1], char_name)) {
if (RuleB(World, EnableReturnHomeButton)) {
int now = time(nullptr);
if ((now - atoi(row[3])) >= RuleI(World, MinOfflineTimeToReturnHome)) {
@ -792,9 +795,8 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
if (home_enabled) {
zone_id = database.MoveCharacterToBind(charid, 4);
}
else {
LogInfo("[{}] is trying to go home before they're able", char_name);
} else {
LogInfo("[{}] is trying to go home before they're able.", char_name);
database.SetHackerFlag(GetAccountName(), char_name, "MQGoHome: player tried to go home before they were able.");
eqs->Close();
return true;
@ -804,9 +806,12 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
/* Check Tutorial*/
if (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial)) {
bool tutorial_enabled = false;
for (auto row = tgh_results.begin(); row != tgh_results.end(); ++row) {
if (strcasecmp(row[1], char_name) == 0) {
if (RuleB(World, EnableTutorialButton) && ((uint8)atoi(row[2]) <= RuleI(World, MaxLevelForTutorial))) {
for (auto row : results) {
if (!strcasecmp(row[1], char_name)) {
if (
RuleB(World, EnableTutorialButton) &&
std::stoi(row[2]) <= RuleI(World, MaxLevelForTutorial)
) {
tutorial_enabled = true;
break;
}
@ -816,9 +821,8 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
if (tutorial_enabled) {
zone_id = RuleI(World, TutorialZoneID);
database.MoveCharacterToZone(charid, zone_id);
}
else {
LogInfo("[{}] is trying to go to tutorial but are not allowed", char_name);
} else {
LogInfo("[{}] is trying to go to the Tutorial but they are not allowed.", char_name);
database.SetHackerFlag(GetAccountName(), char_name, "MQTutorial: player tried to enter the tutorial without having tutorial enabled for this character.");
eqs->Close();
return true;
@ -826,17 +830,17 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
}
}
if (zone_id == 0 || !ZoneName(zone_id)) {
if (!zone_id || !ZoneName(zone_id)) {
// This is to save people in an invalid zone, once it's removed from the DB
database.MoveCharacterToZone(charid, ZoneID("arena"));
LogInfo("Zone not found in database zone_id=[{}], moveing char to arena character:[{}]", zone_id, char_name);
LogInfo("Zone [{}] not found, moving [{}] to Arena.", zone_id, char_name);
}
if(instance_id > 0)
{
if (!database.VerifyInstanceAlive(instance_id, GetCharID()) ||
!database.VerifyZoneInstance(zone_id, instance_id))
{
if (instance_id) {
if (
!database.VerifyInstanceAlive(instance_id, GetCharID()) ||
!database.VerifyZoneInstance(zone_id, instance_id)
) {
zone_id = database.MoveCharacterToInstanceSafeReturn(charid, zone_id, instance_id);
instance_id = 0;
}
@ -845,83 +849,80 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
if(!is_player_zoning) {
database.SetGroupID(char_name, 0, charid);
database.SetLoginFlags(charid, false, false, 1);
}
else{
uint32 groupid = database.GetGroupID(char_name);
if(groupid > 0){
char* leader = 0;
char leaderbuf[64] = {0};
if((leader = database.GetGroupLeaderForLogin(char_name, leaderbuf)) && strlen(leader)>1){
auto outapp3 = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct));
GroupJoin_Struct* gj=(GroupJoin_Struct*)outapp3->pBuffer;
} else {
auto group_id = database.GetGroupID(char_name);
if (group_id) {
auto leader_name = database.GetGroupLeaderForLogin(char_name);
if (!leader_name.empty()) {
auto pack = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct));
auto gj = (GroupJoin_Struct*) pack->pBuffer;
gj->action=8;
strcpy(gj->yourname, char_name);
strcpy(gj->membername, leader);
QueuePacket(outapp3);
safe_delete(outapp3);
strn0cpy(gj->yourname, char_name, sizeof(gj->yourname));
strn0cpy(gj->membername, leader_name.c_str(), sizeof(gj->membername));
QueuePacket(pack);
safe_delete(pack);
}
}
}
outapp = new EQApplicationPacket(OP_MOTD);
std::string tmp;
if (database.GetVariable("MOTD", tmp)) {
outapp->size = tmp.length() + 1;
auto outapp = new EQApplicationPacket(OP_MOTD);
std::string motd_message;
if (database.GetVariable("MOTD", motd_message)) {
outapp->size = motd_message.length() + 1;
outapp->pBuffer = new uchar[outapp->size];
memset(outapp->pBuffer,0,outapp->size);
strcpy((char*)outapp->pBuffer, tmp.c_str());
} else {
// Null Message of the Day. :)
memset(outapp->pBuffer, 0, outapp->size);
strcpy((char*)outapp->pBuffer, motd_message.c_str());
} else { // Null Message of the Day. :)
outapp->size = 1;
outapp->pBuffer = new uchar[outapp->size];
outapp->pBuffer[0] = 0;
}
QueuePacket(outapp);
safe_delete(outapp);
// set mailkey - used for duration of character session
int MailKey = emu_random.Int(1, INT_MAX);
int mail_key = emu_random.Int(1, INT_MAX);
database.SetMailKey(charid, GetIP(), MailKey);
database.SetMailKey(charid, GetIP(), mail_key);
if (UCSServerAvailable_) {
const WorldConfig *Config = WorldConfig::get();
auto config = WorldConfig::get();
std::string buffer;
EQ::versions::UCSVersion ConnectionType = EQ::versions::ucsUnknown;
auto connection_type = EQ::versions::ucsUnknown;
// chat server packet
switch (GetClientVersion()) {
case EQ::versions::ClientVersion::Titanium:
ConnectionType = EQ::versions::ucsTitaniumChat;
break;
case EQ::versions::ClientVersion::SoF:
ConnectionType = EQ::versions::ucsSoFCombined;
break;
case EQ::versions::ClientVersion::SoD:
ConnectionType = EQ::versions::ucsSoDCombined;
break;
case EQ::versions::ClientVersion::UF:
ConnectionType = EQ::versions::ucsUFCombined;
break;
case EQ::versions::ClientVersion::RoF:
ConnectionType = EQ::versions::ucsRoFCombined;
break;
case EQ::versions::ClientVersion::RoF2:
ConnectionType = EQ::versions::ucsRoF2Combined;
break;
default:
ConnectionType = EQ::versions::ucsUnknown;
break;
case EQ::versions::ClientVersion::Titanium:
connection_type = EQ::versions::ucsTitaniumChat;
break;
case EQ::versions::ClientVersion::SoF:
connection_type = EQ::versions::ucsSoFCombined;
break;
case EQ::versions::ClientVersion::SoD:
connection_type = EQ::versions::ucsSoDCombined;
break;
case EQ::versions::ClientVersion::UF:
connection_type = EQ::versions::ucsUFCombined;
break;
case EQ::versions::ClientVersion::RoF:
connection_type = EQ::versions::ucsRoFCombined;
break;
case EQ::versions::ClientVersion::RoF2:
connection_type = EQ::versions::ucsRoF2Combined;
break;
default:
connection_type = EQ::versions::ucsUnknown;
break;
}
buffer = StringFormat("%s,%i,%s.%s,%c%08X",
Config->ChatHost.c_str(),
Config->ChatPort,
Config->ShortName.c_str(),
buffer = fmt::format("{},{},{}.{},{}{:08X}",
config->ChatHost,
config->ChatPort,
config->ShortName,
GetCharName(),
ConnectionType,
MailKey
static_cast<char>(connection_type),
mail_key
);
outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1));
@ -933,21 +934,21 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
// mail server packet
switch (GetClientVersion()) {
case EQ::versions::ClientVersion::Titanium:
ConnectionType = EQ::versions::ucsTitaniumMail;
break;
default:
// retain value from previous switch
break;
case EQ::versions::ClientVersion::Titanium:
connection_type = EQ::versions::ucsTitaniumMail;
break;
default:
// retain value from previous switch
break;
}
buffer = StringFormat("%s,%i,%s.%s,%c%08X",
Config->MailHost.c_str(),
Config->MailPort,
Config->ShortName.c_str(),
buffer = fmt::format("{},{},{}.{},{}{:08X}",
config->MailHost,
config->MailPort,
config->ShortName,
GetCharName(),
ConnectionType,
MailKey
static_cast<char>(connection_type),
mail_key
);
outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1));

View File

@ -100,67 +100,101 @@ ClientListEntry* ClientList::GetCLE(uint32 iID) {
//Check current CLE Entry IPs against incoming connection
void ClientList::GetCLEIP(uint32 iIP) {
ClientListEntry* countCLEIPs = 0;
void ClientList::GetCLEIP(uint32 in_ip) {
ClientListEntry* cle = nullptr;
LinkedListIterator<ClientListEntry*> iterator(clientlist);
int IPInstances = 0;
int count = 0;
iterator.Reset();
while(iterator.MoreElements()) {
countCLEIPs = iterator.GetData();
if ((countCLEIPs->GetIP() == iIP) && ((countCLEIPs->Admin() < (RuleI(World, ExemptMaxClientsStatus))) || (RuleI(World, ExemptMaxClientsStatus) < 0))) { // If the IP matches, and the connection admin status is below the exempt status, or exempt status is less than 0 (no-one is exempt)
IPInstances++; // Increment the occurences of this IP address
LogClientLogin("Account ID: [{}] Account Name: [{}] IP: [{}]", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str());
while (iterator.MoreElements()) {
cle = iterator.GetData();
if (
cle->GetIP() == in_ip &&
(
cle->Admin() < RuleI(World, ExemptMaxClientsStatus) ||
RuleI(World, ExemptMaxClientsStatus) < 0
)
) { // If the IP matches, and the connection admin status is below the exempt status, or exempt status is less than 0 (no-one is exempt)
auto ip_string = long2ip(cle->GetIP());
count++; // Increment the occurences of this IP address
LogClientLogin("Account ID: [{}] Account Name: [{}] IP: [{}]", cle->LSID(), cle->LSName(), ip_string);
if (RuleB(World, EnableIPExemptions)) {
LogClientLogin("Account ID: [{}] Account Name: [{}] IP: [{}] IP Instances: [{}] Max IP Instances: [{}]", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str(), IPInstances, database.GetIPExemption(long2ip(countCLEIPs->GetIP()).c_str()));
if (IPInstances > database.GetIPExemption(long2ip(countCLEIPs->GetIP()).c_str())) {
if(RuleB(World, IPLimitDisconnectAll)) {
LogClientLogin("Disconnect: All accounts on IP [{}]", long2ip(countCLEIPs->GetIP()).c_str());
DisconnectByIP(iIP);
LogClientLogin(
"Account ID: [{}] Account Name: [{}] IP: [{}] IP Instances: [{}] Max IP Instances: [{}]",
cle->LSID(),
cle->LSName(),
ip_string,
count,
database.GetIPExemption(ip_string)
);
auto exemption_amount = database.GetIPExemption(ip_string);
if (exemption_amount > 0 && count > exemption_amount) {
if (RuleB(World, IPLimitDisconnectAll)) {
LogClientLogin("Disconnect: All Accounts on IP [{}]", ip_string);
DisconnectByIP(in_ip);
return;
} else {
LogClientLogin("Disconnect: Account [{}] on IP [{}]", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str());
countCLEIPs->SetOnline(CLE_Status::Offline);
LogClientLogin("Disconnect: Account [{}] on IP [{}]", cle->LSName(), ip_string);
cle->SetOnline(CLE_Status::Offline);
iterator.RemoveCurrent();
continue;
}
}
} else {
if (IPInstances > (RuleI(World, MaxClientsPerIP))) { // If the number of connections exceeds the lower limit
if (
RuleI(World, MaxClientsPerIP) > 0 &&
count > RuleI(World, MaxClientsPerIP)
) { // If the number of connections exceeds the lower limit
if (RuleB(World, MaxClientsSetByStatus)) { // If MaxClientsSetByStatus is set to True, override other IP Limit Rules
LogClientLogin("Account ID: [{}] Account Name: [{}] IP: [{}] IP Instances: [{}] Max IP Instances: [{}]", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str(), IPInstances, countCLEIPs->Admin());
if (IPInstances > countCLEIPs->Admin()) { // The IP Limit is set by the status of the account if status > MaxClientsPerIP
if(RuleB(World, IPLimitDisconnectAll)) {
LogClientLogin("Disconnect: All accounts on IP [{}]", long2ip(countCLEIPs->GetIP()).c_str());
DisconnectByIP(iIP);
LogClientLogin(
"Account ID: [{}] Account Name: [{}] IP: [{}] IP Instances: [{}] Max IP Instances: [{}]",
cle->LSID(),
cle->LSName(),
ip_string,
count,
cle->Admin()
);
if (count > cle->Admin()) { // The IP Limit is set by the status of the account if status > MaxClientsPerIP
if (RuleB(World, IPLimitDisconnectAll)) {
LogClientLogin("Disconnect: All Accounts on IP [{}]", ip_string);
DisconnectByIP(in_ip);
return;
} else {
LogClientLogin("Disconnect: Account [{}] on IP [{}]", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str());
countCLEIPs->SetOnline(CLE_Status::Offline); // Remove the connection
LogClientLogin("Disconnect: Account [{}] on IP [{}]", cle->LSName(), ip_string);
cle->SetOnline(CLE_Status::Offline); // Remove the connection
iterator.RemoveCurrent();
continue;
}
}
} else if ((countCLEIPs->Admin() < RuleI(World, AddMaxClientsStatus)) || (RuleI(World, AddMaxClientsStatus) < 0)) { // Else if the Admin status of the connection is not eligible for the higher limit, or there is no higher limit (AddMaxClientStatus < 0)
if(RuleB(World, IPLimitDisconnectAll)) {
LogClientLogin("Disconnect: All accounts on IP [{}]", long2ip(countCLEIPs->GetIP()).c_str());
DisconnectByIP(iIP);
} else if (
cle->Admin() < RuleI(World, AddMaxClientsStatus) ||
RuleI(World, AddMaxClientsStatus) < 0
) { // Else if the Admin status of the connection is not eligible for the higher limit, or there is no higher limit (AddMaxClientStatus < 0)
if (RuleB(World, IPLimitDisconnectAll)) {
LogClientLogin("Disconnect: All Accounts on IP [{}]", ip_string);
DisconnectByIP(in_ip);
return;
} else {
LogClientLogin("Disconnect: Account [{}] on IP [{}]", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str());
countCLEIPs->SetOnline(CLE_Status::Offline); // Remove the connection
LogClientLogin("Disconnect: Account [{}] on IP [{}]", cle->LSName(), ip_string);
cle->SetOnline(CLE_Status::Offline); // Remove the connection
iterator.RemoveCurrent();
continue;
}
} else if (IPInstances > RuleI(World, AddMaxClientsPerIP)) { // else they are eligible for the higher limit, but if they exceed that
if(RuleB(World, IPLimitDisconnectAll)) {
LogClientLogin("Disconnect: All accounts on IP [{}]", long2ip(countCLEIPs->GetIP()).c_str());
DisconnectByIP(iIP);
} else if (
RuleI(World, AddMaxClientsPerIP) > 0 &&
count > RuleI(World, AddMaxClientsPerIP)
) { // else they are eligible for the higher limit, but if they exceed that
if (RuleB(World, IPLimitDisconnectAll)) {
LogClientLogin("Disconnect: All Accounts on IP [{}]", ip_string);
DisconnectByIP(in_ip);
return;
} else {
LogClientLogin("Disconnect: Account [{}] on IP [{}]", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str());
countCLEIPs->SetOnline(CLE_Status::Offline); // Remove the connection
LogClientLogin("Disconnect: Account [{}] on IP [{}]", cle->LSName(), ip_string);
cle->SetOnline(CLE_Status::Offline); // Remove the connection
iterator.RemoveCurrent();
continue;
}
@ -168,46 +202,54 @@ void ClientList::GetCLEIP(uint32 iIP) {
}
}
}
iterator.Advance();
}
}
uint32 ClientList::GetCLEIPCount(uint32 iIP) {
ClientListEntry* countCLEIPs = 0;
uint32 ClientList::GetCLEIPCount(uint32 in_ip) {
ClientListEntry* cle = nullptr;
LinkedListIterator<ClientListEntry*> iterator(clientlist);
int IPInstances = 0;
int count = 0;
iterator.Reset();
while (iterator.MoreElements()) {
countCLEIPs = iterator.GetData();
if ((countCLEIPs->GetIP() == iIP) && ((countCLEIPs->Admin() < (RuleI(World, ExemptMaxClientsStatus))) || (RuleI(World, ExemptMaxClientsStatus) < 0)) && countCLEIPs->Online() >= CLE_Status::Online) { // If the IP matches, and the connection admin status is below the exempt status, or exempt status is less than 0 (no-one is exempt)
IPInstances++; // Increment the occurences of this IP address
cle = iterator.GetData();
if (
cle->GetIP() == in_ip &&
(
cle->Admin() < RuleI(World, ExemptMaxClientsStatus) ||
RuleI(World, ExemptMaxClientsStatus) < 0
) &&
cle->Online() >= CLE_Status::Online
) { // If the IP matches, and the connection admin status is below the exempt status, or exempt status is less than 0 (no-one is exempt)
count++; // Increment the occurences of this IP address
}
iterator.Advance();
}
return IPInstances;
return count;
}
void ClientList::DisconnectByIP(uint32 iIP) {
ClientListEntry* countCLEIPs = 0;
void ClientList::DisconnectByIP(uint32 in_ip) {
ClientListEntry* cle = nullptr;
LinkedListIterator<ClientListEntry*> iterator(clientlist);
iterator.Reset();
while(iterator.MoreElements()) {
countCLEIPs = iterator.GetData();
if ((countCLEIPs->GetIP() == iIP)) {
if(strlen(countCLEIPs->name())) {
while (iterator.MoreElements()) {
cle = iterator.GetData();
if (cle->GetIP() == in_ip) {
if (strlen(cle->name())) {
auto pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct));
ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer;
strcpy(skp->adminname, "SessionLimit");
strcpy(skp->name, countCLEIPs->name());
auto skp = (ServerKickPlayer_Struct*) pack->pBuffer;
strn0cpy(skp->adminname, "SessionLimit", sizeof(skp->adminname));
strn0cpy(skp->name, cle->name(), sizeof(skp->name));
skp->adminrank = 255;
zoneserver_list.SendPacket(pack);
safe_delete(pack);
}
countCLEIPs->SetOnline(CLE_Status::Offline);
cle->SetOnline(CLE_Status::Offline);
iterator.RemoveCurrent();
}
iterator.Advance();

View File

@ -57,9 +57,9 @@ public:
ClientListEntry* FindCLEByCharacterID(uint32 iCharID);
ClientListEntry* FindCLEByLSID(uint32 iLSID);
ClientListEntry* GetCLE(uint32 iID);
void GetCLEIP(uint32 iIP);
void GetCLEIP(uint32 in_ip);
uint32 GetCLEIPCount(uint32 iLSAccountID);
void DisconnectByIP(uint32 iIP);
void DisconnectByIP(uint32 in_ip);
void CLCheckStale();
void CLEKeepAlive(uint32 numupdates, uint32* wid);
void CLEAdd(uint32 iLSID, const char* iLoginServerName, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = AccountStatus::Player, uint32 ip = 0, uint8 local=0);

View File

@ -44,6 +44,7 @@
#include "../common/rulesys.h"
#include "../common/platform.h"
#include "../common/crash.h"
#include "../common/misc.h"
#include "client.h"
#include "worlddb.h"
@ -658,7 +659,7 @@ int main(int argc, char **argv)
eqsm.OnNewConnection(
[&stream_identifier](std::shared_ptr<EQ::Net::EQStream> stream) {
stream_identifier.AddStream(stream);
LogInfo("New connection from IP {0}:{1}", stream->GetRemoteIP(), ntohs(stream->GetRemotePort()));
LogInfo("New connection from IP {}:{}", long2ip(stream->GetRemoteIP()), ntohs(stream->GetRemotePort()));
}
);

View File

@ -36,6 +36,7 @@
#include "../common/memory_mapped_file.h"
#include "../common/spdat.h"
#include "../common/eqemu_logsys.h"
#include "../common/misc.h"
#include "api_service.h"
#include "zone_config.h"
@ -514,7 +515,7 @@ int main(int argc, char** argv) {
eqsm->OnNewConnection([&stream_identifier](std::shared_ptr<EQ::Net::EQStream> stream) {
stream_identifier.AddStream(stream);
LogF(Logs::Detail, Logs::WorldServer, "New connection from IP {0}:{1}", stream->GetRemoteIP(), ntohs(stream->GetRemotePort()));
LogInfo("New connection from IP {}:{}", long2ip(stream->GetRemoteIP()), ntohs(stream->GetRemotePort()));
});
}