IP Banning re-write.

Closes #13.
This commit is contained in:
CodeForFame 2011-06-25 16:01:22 -05:00
parent 8bb374fd05
commit 4fcb575620
9 changed files with 345 additions and 245 deletions

View File

@ -27,8 +27,25 @@
<entry key="admins">None</entry>
<entry key="ip-ban-removal-delay">1800000</entry>
<entry key="block-command">sudo route add -host ${ip} reject</entry>
<entry key="unblock-command">sudo route del -host ${ip} reject</entry>
<entry key="os-level-blocking">true</entry>
<entry key="os-level-block-command">sudo route add -host ${ip} reject</entry>
<entry key="os-level-unblock-command">sudo route del -host ${ip} reject</entry>
<entry key="application-level-blocking">true</entry>
<!-- Alert when someone is throttled at the application-level -->
<entry key="application-level-blocking-throttle-alert">false</entry>
<!-- Alert when someone is throttled at the os-level -->
<entry key="os-level-blocking-throttle-alert">true</entry>
<!-- Alert when unblocking at the os-level fails -->
<entry key="os-level-blocking-unblock-failed-alert">true</entry>
<entry key="connection-throttle-size">100</entry>
<!-- This is the threshold of how many connections that an IP can have.
This uses a LRUMap, and only has a limited number of entries, so only the
most active IPs will be stored at a time. -->
<entry key="connection-throttle">5</entry>
<!-- Every 100 minutes -->
<entry key="garbage-collect-interval">6000000</entry>

View File

@ -22,11 +22,11 @@ public class Config {
SERVER_LOCATION, LS_IP;
public static int SERVER_PORT, SERVER_VERSION, MAX_PLAYERS, LS_PORT,
SERVER_NUM;
SERVER_NUM, CONENCTION_THROTTLE_THRESHOLD;
public static long START_TIME;
public static boolean members, f2pWildy;
public static boolean members, f2pWildy, APPLICATION_LEVEL_BLOCKING;
public static double expRate, subExpRate;
@ -34,11 +34,11 @@ public class Config {
public static int IP_BAN_REMOVAL_DELAY;
public static int GARBAGE_COLLECT_INTERVAL;
public static int SAVE_INTERVAL;
public static String DATE_FORMAT;
public static String BLOCK_COMMAND;
public static String UNBLOCK_COMMAND;
public static String ALERT_CONFIG;
public static String COMMAND_CONFIG;
public static String DATE_FORMAT, BLOCK_COMMAND, UNBLOCK_COMMAND,
ALERT_CONFIG, COMMAND_CONFIG;
public static int CONNECTION_THROTTLE_SIZE;
public static boolean OS_LEVEL_BLOCKING, APPLICATION_LEVEL_THROTTLE_ALERT,
OS_LEVEL_THROTTLE_ALERT, OS_LEVEL_UNBLOCK_FAILED_ALERT;
static {
loadEnv();
@ -88,6 +88,20 @@ public class Config {
.getProperty("ip-ban-removal-delay"));
BLOCK_COMMAND = props.getProperty("block-command");
UNBLOCK_COMMAND = props.getProperty("unblock-command");
CONNECTION_THROTTLE_SIZE = Integer.parseInt(props
.getProperty("connection-throttle-size"));
CONENCTION_THROTTLE_THRESHOLD = Integer.parseInt(props
.getProperty("connection-throttle"));
APPLICATION_LEVEL_BLOCKING = Boolean.parseBoolean(props
.getProperty("application-level-blocking"));
OS_LEVEL_BLOCKING = Boolean.parseBoolean(props
.getProperty("os-level-blocking"));
APPLICATION_LEVEL_THROTTLE_ALERT = Boolean.parseBoolean(props
.getProperty("application-level-blocking-throttle-alert"));
OS_LEVEL_THROTTLE_ALERT = Boolean.parseBoolean(props
.getProperty("os-level-blocking-throttle-alert"));
OS_LEVEL_UNBLOCK_FAILED_ALERT = Boolean.parseBoolean(props
.getProperty("os-level-blocking-unblock-failed-alert"));
GARBAGE_COLLECT_INTERVAL = Integer.parseInt(props
.getProperty("garbage-collect-interval"));

View File

@ -12,9 +12,16 @@ import org.apache.commons.collections.map.LRUMap;
*/
public class Cache<K, V> {
// Shitty commons and their failure to support generics...
private Map<K, V> cache;
public Cache() {
this(100);
}
@SuppressWarnings("unchecked")
private Map<K, V> cache = new LRUMap();
public Cache(int size) {
cache = new LRUMap(size);
}
public V get(K key) {
return cache.get(key);

View File

@ -15,18 +15,7 @@ import org.moparscape.msc.gs.util.Logger;
* Handles the protocol events fired from MINA.
*/
public class RSCConnectionHandler implements IoHandler {
/**
* World instance
*/
/*
*
* private int attackers = 0;
*
* private Map<InetAddress, Long> clients; private Set<InetAddress>
* connectedAddresses; private Map<InetAddress, Integer> counts; private
* long lastAttack = 0;
*/
/**
* A reference to the game engine's packet queue
*/
@ -41,22 +30,8 @@ public class RSCConnectionHandler implements IoHandler {
*/
public RSCConnectionHandler(GameEngine engine) {
packets = (PacketQueue<RSCPacket>) engine.getPacketQueue();
/*
* clients = Collections.synchronizedMap(new HashMap<InetAddress,
* Long>()); counts = Collections.synchronizedMap(new
* HashMap<InetAddress, Integer>()); written =
* Collections.synchronizedMap(new HashMap<InetAddress, Integer>());
* connectedAddresses = new HashSet<InetAddress>();
*/
}
/*
* public void connectionOk(IoSession io) { counts.remove(getAddress(io)); }
*
* public void delayClient(IoSession session, int delay) { long d =
* System.currentTimeMillis() - delay; clients.put(getAddress(session), d);
* }
*/
/**
* Invoked whenever an exception is thrown by MINA or this IoHandler.
*
@ -67,69 +42,13 @@ public class RSCConnectionHandler implements IoHandler {
*/
public void exceptionCaught(IoSession session, Throwable cause) {
Player p = (Player) session.getAttachment();
// if(p.getUsername().equalsIgnoreCase("xent")) {
// }
if (p != null)
p.getActionSender().sendLogout();
session.close();
/*
* Logging.debug("---MINA Error from: " + p.getUsername() + " -------");
* cause.printStackTrace();Logging.debug(
* "------------------------------------------------------------");
*/
cause.printStackTrace();
}
/**
* Method responsible for deciding if a connection is OK to continue
*
* @param session
* The new session that will be verified
* @return True if the session meets the criteria, otherwise false (if
* false, adds to a IPSec filter & writes IP to log.
*/
/*
* public boolean isConnectionOk(IoSession session) { final InetAddress addr
* = getAddress(session); final String ip = addr.toString().replaceAll("/",
* ""); // ip = ip.replaceAll("/",""); long now =
* System.currentTimeMillis(); int c = 0; if (counts.containsKey(addr) &&
* clients.containsKey(addr)) { try { c = counts.get(addr); } catch
* (Exception e) { Logging.debug("Error: " + e); } if (c >= 5) { if
* (!written.containsKey(addr)) { attackers++; // if(now - lastAttack >
* 60000) // { // for(Player p : world.getPlayers()) // { // p.save(); //
* p.getActionSender().sendLogout(); // } //
* Instance.getServer().getLoginConnector
* ().getActionSender().saveProfiles(); // } try { /*
* Logging.debug("ATTACKER IP: " + ip); BufferedWriter bf2 = new
* BufferedWriter(new FileWriter( "ddos.log", true));
* bf2.write("sudo /sbin/route add " + addr.getHostAddress() +
* " gw 127.0.0.1"); bf2.newLine(); bf2.close(); / written.put(addr, 1);
* Instance.getDelayedEventHandler().add(new DelayedEvent(null, 3600000) {
* public void run() { written.remove(addr); counts.remove(addr); try {
* Runtime.getRuntime().exec("sudo /sbin/route delete " + ip); } catch
* (Exception err) { Logging.debug(err); } } }); try {
* Runtime.getRuntime().exec("sudo /sbin/route add " + ip +
* " gw 127.0.0.1"); } catch (Exception err) { Logging.debug(err); }
*
* // try { Runtime.getRuntime().exec(ip + ".bat"); } // catch (Exception
* err) { Logging.debug(err); } lastAttack = now; } catch (Exception e) {
* System.err.println(e); } } return false; } } if
* (clients.containsKey(addr)) { long lastConnTime = clients.get(addr); if
* (now - lastConnTime < 2000) { if (!counts.containsKey(addr))
* counts.put(addr, 0); else c = counts.get(addr) + 1; counts.put(addr, c);
* return false; } else { clients.put(addr, now); if
* (counts.containsKey(addr)) counts.remove(addr); return true; } } else {
* clients.put(addr, now); return true; } }
*/
/**
* Invoked whenever a packet is ready to be added to the queue.
*
* @param session
* The IO session on which the packet was received
* @param message
* The packet
*/
public void messageReceived(IoSession session, Object message) {
Player player = (Player) session.getAttachment();
if (session.isClosing() || player.destroyed()) {
@ -137,12 +56,6 @@ public class RSCConnectionHandler implements IoHandler {
}
RSCPacket p = (RSCPacket) message;
/*
* if(p.getID() == 57 || p.getID() == 73 || p.getID() == 40 || p.getID()
* == 51 || p.getID() == 128 || p.getID() == 206 || p.getID() == 71 ||
* p.getID() == 55)
*/
if (p.getID() == 55)
player.addInterval();
@ -176,14 +89,9 @@ public class RSCConnectionHandler implements IoHandler {
}
public void sessionCreated(IoSession session) {
/*
* if (!isConnectionOk(session)) { session.close(); return; } else {
* connectionOk(session); }
*/
session.getFilterChain().addFirst("protocolFilter",
new ProtocolCodecFilter(new RSCCodecFactory()));
// Logger.event("Connection from: " +
// ((InetSocketAddress)session.getRemoteAddress()).getAddress().getHostAddress());
Logger.println("Connection from: "
+ ((InetSocketAddress) session.getRemoteAddress()).getAddress()
.getHostAddress());

View File

@ -6,21 +6,25 @@ import java.net.SocketAddress;
import org.apache.mina.common.IoFilter;
import org.apache.mina.common.IoSession;
import org.apache.mina.filter.BlacklistFilter;
import org.moparscape.msc.config.Config;
import org.moparscape.msc.gs.util.Cache;
public class ConnectionFilter extends BlacklistFilter {
private Cache<InetSocketAddress, Integer> connections = new Cache<InetSocketAddress, Integer>();
private static final int BLOCK_THRESHOLD = 5;
private Cache<InetSocketAddress, Integer> connections = new Cache<InetSocketAddress, Integer>(
Config.CONNECTION_THROTTLE_SIZE);
public void sessionCreated(IoFilter.NextFilter nextFilter, IoSession session) {
final SocketAddress sa = session.getRemoteAddress();
if (sa != null && sa instanceof InetSocketAddress) {
final InetSocketAddress a = (InetSocketAddress) sa;
if(IPBanManager.isBlocked(a)) {
block(a.getAddress());
return;
}
final Integer val = connections.get(a);
System.out.println(val);
final Integer retVal = connections
.put(a, val == null ? 1 : val + 1);
if (retVal != null && retVal > BLOCK_THRESHOLD) {
if (retVal != null && retVal > Config.CONENCTION_THROTTLE_THRESHOLD) {
block(a.getAddress());
}
}
@ -33,10 +37,10 @@ public class ConnectionFilter extends BlacklistFilter {
if (sa != null && sa instanceof InetSocketAddress) {
final InetSocketAddress a = (InetSocketAddress) sa;
final Integer val = connections.get(a);
System.out.println(val);
final Integer retVal = connections
.put(a, val == null ? 1 : val + 1);
if (retVal != null && retVal - 1 <= BLOCK_THRESHOLD) {
if (retVal != null
&& retVal - 1 <= Config.CONENCTION_THROTTLE_THRESHOLD) {
unblock(a.getAddress());
}
}

View File

@ -0,0 +1,179 @@
package org.moparscape.msc.gs.connection.filter;
import java.util.List
import java.util.concurrent.CopyOnWriteArrayList
import org.moparscape.msc.gs.Server
import org.moparscape.msc.gs.core.DelayedEventHandler
import org.moparscape.msc.gs.event.DelayedEvent
import org.moparscape.msc.gs.util.Logger
import org.moparscape.msc.config.Config
import org.moparscape.msc.gs.alert.AlertHandler
import java.net.InetSocketAddress
import java.net.SocketAddress
import scala.collection.JavaConversions._
object IPBanManager extends Blocker {
override def isBlocked(ip: String) = {
var v = false
if (Config.APPLICATION_LEVEL_BLOCKING)
v = ApplicationLevelBlocking.isBlocked(ip)
if (Config.OS_LEVEL_BLOCKING)
v = v || OSLevelBlocking.isBlocked(ip)
v
}
def isBlocked(ip: SocketAddress): Boolean = {
isBlocked(lookupIP(ip))
}
override def throttle(ip: String) {
if (Config.APPLICATION_LEVEL_BLOCKING)
ApplicationLevelBlocking.throttle(ip)
if (Config.OS_LEVEL_BLOCKING)
OSLevelBlocking.throttle(ip)
}
def throttle(ip: java.util.List[String]) {
ip foreach { throttle(_) }
}
def throttle(ip: SocketAddress) {
throttle(lookupIP(ip))
}
override def block(ip: String) {
if (ip != null && ip.length > 0) {
if (Config.APPLICATION_LEVEL_BLOCKING)
ApplicationLevelBlocking.block(ip)
if (Config.OS_LEVEL_BLOCKING)
OSLevelBlocking.block(ip)
}
}
def block(ip: java.util.List[String]) {
ip foreach { block(_) }
}
def block(ip: SocketAddress) {
block(lookupIP(ip))
}
override def unblock(ip: String) {
ApplicationLevelBlocking.unblock(ip)
OSLevelBlocking.unblock(ip)
}
def unblock(ip: SocketAddress) {
unblock(lookupIP(ip))
}
private def lookupIP(sa: SocketAddress): String = {
if (sa != null && sa.isInstanceOf[InetSocketAddress]) {
val a = sa.asInstanceOf[InetSocketAddress]
return a.getAddress.getHostAddress
}
return null
}
}
trait Blocker {
def isBlocked(ip: String): Boolean
def block(ip: String)
def unblock(ip: String)
def throttle(ip: String)
}
private object ApplicationLevelBlocking extends Blocker {
private val blocked = new CopyOnWriteArrayList[String];
private val throttled = new CopyOnWriteArrayList[String]
private val events = Server.getServer().getEngine().getEventHandler()
override def isBlocked(ip: String) = {
if (blocked.contains(ip)) {
true
}
false
}
override def block(ip: String) {
blocked.addIfAbsent(ip)
}
override def unblock(ip: String) {
blocked.remove(ip)
}
override def throttle(ip: String) {
if (!throttled.contains(ip)) {
events.add(new DelayedEvent(null, Config.IP_BAN_REMOVAL_DELAY) {
override def run() {
unblock(ip)
throttled.remove(ip)
Logger.println("Application - Unblocked " + ip)
}
})
block(ip)
throttled.add(ip)
if (Config.APPLICATION_LEVEL_THROTTLE_ALERT)
AlertHandler.sendAlert("Application - Throttled " + ip, throttled.size / 10)
Logger.println("Application - Throttled " + ip)
}
}
}
private object OSLevelBlocking extends Blocker {
private val throttled = new CopyOnWriteArrayList[String]
private val blocked = new CopyOnWriteArrayList[String]
private val events = Server.getServer().getEngine().getEventHandler()
override def isBlocked(ip: String) = {
if (blocked.contains(ip)) {
true
}
false
}
override def throttle(ip: String) {
if (!throttled.contains(ip)) {
events.add(new DelayedEvent(null, Config.IP_BAN_REMOVAL_DELAY) {
override def run() {
unblock(ip)
throttled.remove(ip)
}
})
block(ip)
throttled.add(ip)
if (Config.OS_LEVEL_THROTTLE_ALERT)
AlertHandler.sendAlert("OS - Throttled " + ip, throttled.size / 10)
Logger.println("OS - Throttled " + ip)
}
}
override def block(ip: String) {
Runtime.getRuntime.exec(Config.BLOCK_COMMAND.replaceAll("${ip}", ip))
blocked addIfAbsent ip
}
override def unblock(ip: String) {
try {
Runtime.getRuntime.exec(Config.UNBLOCK_COMMAND.replaceAll("${ip}", ip))
blocked remove ip
throttled.remove(ip)
Logger.println("OS - Unblocked " + ip)
} catch {
case e: Exception => {
Logger.println("OS - Failed to unblock " + ip)
Logger.error(e)
if (Config.OS_LEVEL_UNBLOCK_FAILED_ALERT)
AlertHandler.sendAlert("OS - Failed to unblock " + ip, 2)
}
}
}
}

View File

@ -1,42 +0,0 @@
package org.moparscape.msc.gs.connection.filter;
import java.util.List
import java.util.concurrent.CopyOnWriteArrayList
import org.moparscape.msc.gs.Server
import org.moparscape.msc.gs.core.DelayedEventHandler
import org.moparscape.msc.gs.event.DelayedEvent
import org.moparscape.msc.gs.util.Logger
import org.moparscape.msc.config.Config
import org.moparscape.msc.gs.alert.AlertHandler
object OSLevelBlocking {
private val blocked = new CopyOnWriteArrayList[String];
private val events = Server.getServer().getEngine().getEventHandler()
def block(ip: String) {
if (!blocked.contains(ip)) {
events.add(new DelayedEvent(null, Config.IP_BAN_REMOVAL_DELAY) {
def run() {
try {
Runtime.getRuntime.exec(Config.UNBLOCK_COMMAND.replaceAll("${ip}", ip));
blocked.remove(ip)
Logger.println("Unblocked " + ip)
} catch {
case e: Exception => {
Logger.error(e)
Logger.println("Failed to unblock " + ip)
AlertHandler.sendAlert("Failed to unblock " + ip, 2);
}
}
}
})
Runtime.getRuntime.exec(Config.BLOCK_COMMAND.replaceAll("${ip}", ip));
blocked.add(ip)
AlertHandler.sendAlert("Blocked " + ip, blocked.size / 10);
Logger.println("Blocked " + ip)
}
}
}

View File

@ -228,8 +228,7 @@ public final class GameEngine extends Thread {
if (player.getUsername() == null && p.getID() != 32
&& p.getID() != 77 && p.getID() != 0) {
final String ip = player.getCurrentIP();
// flagSession(session);
OSLevelBlocking.block(ip);
OSLevelBlocking.throttle(ip);
continue;
}
PacketHandler handler = packetHandlers.get(p.getID());

View File

@ -1,92 +1,106 @@
<PacketHandler-array>
<PacketHandler>
<ids>
<int>1</int>
<int>2</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.RegisterWorld</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>3</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.KillHandler</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>4</int>
<int>5</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.BanHandler</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>6</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.PrivacySettingHandler</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>7</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.GameSettingHandler</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>8</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.PlayerInfoRequestHandler</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>9</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.SaveProfilesRequestHandler</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>10</int>
<int>11</int>
<int>12</int>
<int>13</int>
<int>14</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.FriendHandler</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>15</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.ReportHandler</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>20</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.PlayerSaveHandler</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>30</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.PlayerLogoutHandler</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>31</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.PlayerLoginHandler</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>32</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.LogHandler</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>33</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.Tradelog</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>1</int>
<int>2</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.RegisterWorld
</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>3</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.KillHandler
</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>4</int>
<int>5</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.BanHandler
</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>6</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.PrivacySettingHandler
</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>7</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.GameSettingHandler
</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>8</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.PlayerInfoRequestHandler
</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>9</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.SaveProfilesRequestHandler
</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>10</int>
<int>11</int>
<int>12</int>
<int>13</int>
<int>14</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.FriendHandler
</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>15</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.ReportHandler
</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>20</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.PlayerSaveHandler
</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>30</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.PlayerLogoutHandler
</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>31</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.PlayerLoginHandler
</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>32</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.LogHandler
</className>
</PacketHandler>
<PacketHandler>
<ids>
<int>33</int>
</ids>
<className>org.moparscape.msc.ls.packethandler.loginserver.Tradelog
</className>
</PacketHandler>
</PacketHandler-array>