PDA

View Full Version : Limiting client connections by IP address (Integrated into Rules System)


TheLieka
03-24-2008, 06:48 PM
I've seen several people asking / talking about limiting client connections by IP address lately, so I figure I'd just post my implementation of it. Give it a try and let me know what you think.

.\common\ruletypes.h


Change:
RULE_CATEGORY( World )
RULE_INT ( World, ZoneAutobootTimeoutMS, 60000 )
RULE_INT ( World, ClientKeepaliveTimeoutMS, 65000 )
RULE_CATEGORY_END()

To:
RULE_CATEGORY( World )
RULE_INT ( World, ZoneAutobootTimeoutMS, 60000 )
RULE_INT ( World, ClientKeepaliveTimeoutMS, 65000 )
RULE_INT ( World, MaxClientsPerIP, -1 ) //Lieka Edit: Maximum number of clients allowed to connect per IP address. Default value: -1 (feature disabled)
RULE_INT ( World, ExemptMaxClientsStatus, -1 ) //Lieka Edit: Exempt accounts from the MaxClientsPerIP rule, if their status is >= this value. Default value: -1 (feature disabled)
RULE_CATEGORY_END()


.\world\clientlist.h


Change:
void ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl);
void CLERemoveZSRef(ZoneServer* iZS);
ClientListEntry* CheckAuth(int32 iLSID, const char* iKey);
ClientListEntry* CheckAuth(const char* iName, const char* iPassword);
ClientListEntry* CheckAuth(int32 id, const char* iKey, int32 ip);
ClientListEntry* FindCharacter(const char* name);
ClientListEntry* FindCLEByAccountID(int32 iAccID);
ClientListEntry* FindCLEByCharacterID(int32 iAccID);
ClientListEntry* GetCLE(int32 iID);
void CLCheckStale();
void CLEKeepAlive(int32 numupdates, int32* wid);
void CLEAdd(int32 iLSID, const char* iLoginName, const char* iLoginKey, sint16 iWorldAdmin = 0, int32 ip = 0, uint8 local=0);
void UpdateClientGuild(int32 char_id, int32 guild_id);

To:

void ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl);
void CLERemoveZSRef(ZoneServer* iZS);
ClientListEntry* CheckAuth(int32 iLSID, const char* iKey);
ClientListEntry* CheckAuth(const char* iName, const char* iPassword);
ClientListEntry* CheckAuth(int32 id, const char* iKey, int32 ip);
ClientListEntry* FindCharacter(const char* name);
ClientListEntry* FindCLEByAccountID(int32 iAccID);
ClientListEntry* FindCLEByCharacterID(int32 iAccID);
ClientListEntry* GetCLE(int32 iID);
void GetCLEIP(int32 iIP);
void CLCheckStale();
void CLEKeepAlive(int32 numupdates, int32* wid);
void CLEAdd(int32 iLSID, const char* iLoginName, const char* iLoginKey, sint16 iWorldAdmin = 0, int32 ip = 0, uint8 local=0);
void UpdateClientGuild(int32 char_id, int32 guild_id);

.\world\clientlist.cpp

After:
void ClientList::CLERemoveZSRef(ZoneServer* iZS) {
LinkedListIterator<ClientListEntry*> iterator(clientlist);

iterator.Reset();
while(iterator.MoreElements()) {
if (iterator.GetData()->Server() == iZS) {
iterator.GetData()->ClearServer(); // calling this before LeavingZone() makes CLE not update the number of players in a zone
iterator.GetData()->LeavingZone();
}
iterator.Advance();
}
}

ClientListEntry* ClientList::GetCLE(int32 iID) {
LinkedListIterator<ClientListEntry*> iterator(clientlist);


iterator.Reset();
while(iterator.MoreElements()) {
if (iterator.GetData()->GetID() == iID) {
return iterator.GetData();
}
iterator.Advance();
}
return 0;
}

Add:
//Lieka Edit Begin: Check current CLE Entry IPs against incoming connection

void ClientList::GetCLEIP(int32 iIP) {
ClientListEntry* countCLEIPs = 0;
LinkedListIterator<ClientListEntry*> iterator(clientlist);

int IPInstances = 0;
iterator.Reset();
while(iterator.MoreElements()) {
countCLEIPs = iterator.GetData();
if ((countCLEIPs->GetIP() == iIP) && ((countCLEIPs->Admin() <= (RuleI(World, ExemptMaxClientsStatus))) || (RuleI(World, ExemptMaxClientsStatus) < 0)) {
IPInstances++;
if (IPInstances > (RuleI(World, MaxClientsPerIP)){
countCLEIPs->SetOnline(CLE_Status_Offline);
iterator.RemoveCurrent();
}
}
iterator.Advance();
}
}
//Lieka Edit End


.\world\client.cpp


After:
case OP_EnterWorld: // Enter world
{
if (GetAccountID() == 0) {
clog(WORLD__CLIENT_ERR,"Enter world with no logged in account");
eqs->Close();
break;
}
if(GetAdmin() < 0)
{
clog(WORLD__CLIENT,"Account banned or suspended.");
eqs->Close();
break;
}

Add:

if (RuleI(World, MaxClientsPerIP) >= 0) {
client_list.GetCLEIP(this->GetIP()); //Lieka Edit Begin: Check current CLE Entry IPs against incoming connection
}

Required SQL:
Insert into rule_values values (0, 'World:MaxClientsPerIP', -1);
Insert into rule_values values (0, 'World:ExemptMaxClientsStatus', -1);

There are 2 new rule values.
World:MaxClientsPerIP = Maximum number of simultaneous EQ Client connections allowed per IP address. Set the rule value to -1 to disable this feature.
World:ExemptMaxClientsStatus = Minimum Account status to exempt the MaxClientsPerIP rule. This is helpful for the inevitable random family of 16 that live together, and all want to play on your server, as well as GMs, Devs, and QA staff. Again, set the rule value to -1 to disable this feature.

It's commented throughout, but let me know if you have questions.

Thanks,
Dax

Semedaien
03-25-2008, 06:03 AM
very much appreciated sir, will get back to you on how it works ;)

krusher
03-25-2008, 08:33 AM
Would it now be possible to BAN by IP with some modifications to this code. I know we can ban by account name but it sure would be handy to have a table with banned IP's to check against the login process.

Thanks

Krusher

trevius
03-25-2008, 09:17 AM
Wow! This would be an awesome addition to the next patch update! *hint hint* Very nice work yet again!

If the new rules you have been making and the new stuff KLS has been working on all goes in, this will be the best code update in a while! Getting a little antsy :D

Also, the idea Krusher has is a great one too! You would need a new table for it to reference for IPs. But then again, I wouldn't mind having IP addresses listed in a new table that matches accounts to IPs. It would be awesome for keeping track of the "bad" players. As of now, I have to catch them when they are on and do a #iplookup. If this table was put in, you could then just have a field for blocking that IP. Maybe even a new in-game command #ipblock <IP Address>.

I have IP blockers and even some built into my router, but it would be nice if it could just be done directly from the server tables.

TheLieka
03-25-2008, 06:23 PM
I've been thinking about doing that for a while, but haven't gotten around to coding it. Right now we just ban IPs at the firewall when we need to, but being able to do it from within the database would be a pretty nice addition. I might just look into that.

Dax

MNWatchdog
03-25-2008, 06:57 PM
Thanks for your code Dax. I wish more people would post thier code changes for others who might want to integrate them.

MNWatchdog
03-25-2008, 07:03 PM
Would it now be possible to BAN by IP with some modifications to this code. I know we can ban by account name but it sure would be handy to have a table with banned IP's to check against the login process.

Thanks

KrusherSounds like a seed of a new thread.

Although, I think blocking at the router is probably a better idea. Dont even let them near your server. Keeps the vengeful hackers further away. Only down side is if your router either doesnt support this or you run out of blockable IPs in the router.

Angelox
03-26-2008, 03:16 AM
You see, that's what I'm talking about!
Thanks for the fix, I hope we get it into the source soon

Jibbatwinkers
03-26-2008, 09:17 AM
Excited for this to go in, thanks for sharing!

ChaosSlayer
03-26-2008, 11:27 AM
/applauds

Boxers! Beware.. Mha-ha-ha-ha :cool:

TheLieka
03-26-2008, 06:17 PM
Would it now be possible to BAN by IP with some modifications to this code. I know we can ban by account name but it sure would be handy to have a table with banned IP's to check against the login process.

Thanks

Krusher

Try this out:

http://www.eqemulator.net/forums/showthread.php?t=24748

TheLieka
04-02-2008, 06:03 PM
I was double checking this submission against the current release, and saw that I had a couple of issues with it. Here are the corrections. Sorry about that. ;)

This portion had a typo in it:
.\world\clientlist.cpp

After:

void ClientList::CLERemoveZSRef(ZoneServer* iZS) {
LinkedListIterator<ClientListEntry*> iterator(clientlist);

iterator.Reset();
while(iterator.MoreElements()) {
if (iterator.GetData()->Server() == iZS) {
iterator.GetData()->ClearServer(); // calling this before LeavingZone() makes CLE not update the number of players in a zone
iterator.GetData()->LeavingZone();
}
iterator.Advance();
}
}

ClientListEntry* ClientList::GetCLE(int32 iID) {
LinkedListIterator<ClientListEntry*> iterator(clientlist);


iterator.Reset();
while(iterator.MoreElements()) {
if (iterator.GetData()->GetID() == iID) {
return iterator.GetData();
}
iterator.Advance();
}
return 0;
}



Add:

//Lieka Edit Begin: Check current CLE Entry IPs against incoming connection

void ClientList::GetCLEIP(int32 iIP) {
ClientListEntry* countCLEIPs = 0;
LinkedListIterator<ClientListEntry*> iterator(clientlist);

int IPInstances = 0;
iterator.Reset();
while(iterator.MoreElements()) {
countCLEIPs = iterator.GetData();
if ((countCLEIPs->GetIP() == iIP) && ((countCLEIPs->Admin() <= (RuleI(World, ExemptMaxClientsStatus))) || (RuleI(World, ExemptMaxClientsStatus) < 0))) {
IPInstances++;
if (IPInstances > (RuleI(World, MaxClientsPerIP))){
countCLEIPs->SetOnline(CLE_Status_Offline);
iterator.RemoveCurrent();
}
}
iterator.Advance();
}
}
//Lieka Edit End

I left this part out:
.\world\clientlist.h

After:

#include "../common/eq_packet_structs.h"
#include "../common/linked_list.h"
#include "../common/timer.h"

Add:
#include "../common/rulesys.h"

Dax

Angelox
04-07-2008, 03:30 PM
Please! I don't want to shut this thread down - post here only if you have a reasonable fix to this submission. Other than that, you can post comments here;
http://eqemulator.net/forums/showthread.php?t=24787&page=2
Again, this forum is intended for submissions only