added rate limiting, added connections per ip limit
This commit is contained in:
parent
f26e1b8f6d
commit
7e3aee8699
|
@ -20,12 +20,14 @@ public class EaglerSPClient {
|
|||
public final long createdOn;
|
||||
public boolean serverNotifiedOfClose = false;
|
||||
public LoginState state = LoginState.INIT;
|
||||
public final String address;
|
||||
|
||||
EaglerSPClient(WebSocket sock, EaglerSPServer srv, String id) {
|
||||
EaglerSPClient(WebSocket sock, EaglerSPServer srv, String id, String addr) {
|
||||
this.socket = sock;
|
||||
this.server = srv;
|
||||
this.id = id;
|
||||
this.createdOn = System.currentTimeMillis();
|
||||
this.address = addr;
|
||||
}
|
||||
|
||||
public void send(IPacket packet) {
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.java_websocket.WebSocket;
|
|||
import org.java_websocket.handshake.ClientHandshake;
|
||||
import org.java_websocket.server.WebSocketServer;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.relay.RateLimiter.RateLimit;
|
||||
import net.lax1dude.eaglercraft.sp.relay.pkt.*;
|
||||
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacket07LocalWorlds.LocalWorld;
|
||||
|
||||
|
@ -26,6 +27,9 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
public static EaglerSPRelay instance;
|
||||
public static final EaglerSPRelayConfig config = new EaglerSPRelayConfig();
|
||||
|
||||
private static RateLimiter pingRateLimiter = null;
|
||||
private static RateLimiter worldRateLimiter = null;
|
||||
|
||||
public static final DebugLogger logger = DebugLogger.getLogger("EaglerSPRelay");
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
|
@ -35,9 +39,24 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
logger.debug("Debug logging enabled");
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Starting EaglerSPRelay version {}...", Constants.versionName);
|
||||
config.load(new File("relayConfig.ini"));
|
||||
|
||||
if(config.isPingRateLimitEnable()) {
|
||||
pingRateLimiter = new RateLimiter(config.getPingRateLimitPeriod() * 1000,
|
||||
config.getPingRateLimitLimit(), config.getPingRateLimitLockoutLimit(),
|
||||
config.getPingRateLimitLockoutDuration() * 1000);
|
||||
}
|
||||
|
||||
if(config.isWorldRateLimitEnable()) {
|
||||
worldRateLimiter = new RateLimiter(config.getWorldRateLimitPeriod() * 1000,
|
||||
config.getWorldRateLimitLimit(), config.getWorldRateLimitLockoutLimit(),
|
||||
config.getWorldRateLimitLockoutDuration() * 1000);
|
||||
}
|
||||
|
||||
EaglerSPRelayConfigRelayList.loadRelays(new File("relays.txt"));
|
||||
|
||||
logger.info("Starting WebSocket Server...");
|
||||
instance = new EaglerSPRelay(new InetSocketAddress(config.getAddress(), config.getPort()));
|
||||
instance.setConnectionLostTimeout(20);
|
||||
|
@ -45,6 +64,7 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
instance.start();
|
||||
|
||||
Thread tickThread = new Thread((() -> {
|
||||
int rateLimitUpdateCounter = 0;
|
||||
while(true) {
|
||||
try {
|
||||
long millis = System.currentTimeMillis();
|
||||
|
@ -52,7 +72,7 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
Iterator<Entry<WebSocket,PendingConnection>> itr = pendingConnections.entrySet().iterator();
|
||||
while(itr.hasNext()) {
|
||||
Entry<WebSocket,PendingConnection> etr = itr.next();
|
||||
if(millis - etr.getValue().openTime > 1000l) {
|
||||
if(millis - etr.getValue().openTime > 500l) {
|
||||
etr.getKey().close();
|
||||
itr.remove();
|
||||
}
|
||||
|
@ -62,16 +82,25 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
Iterator<EaglerSPClient> itr = clientConnections.values().iterator();
|
||||
while(itr.hasNext()) {
|
||||
EaglerSPClient cl = itr.next();
|
||||
if(millis - cl.createdOn > 500l) {
|
||||
if(millis - cl.createdOn > 5000l) {
|
||||
cl.disconnect(IPacketFEDisconnectClient.TYPE_TIMEOUT, "Took too long to connect!");
|
||||
}
|
||||
}
|
||||
}
|
||||
if(++rateLimitUpdateCounter > 300) {
|
||||
if(pingRateLimiter != null) {
|
||||
pingRateLimiter.update();
|
||||
}
|
||||
if(worldRateLimiter != null) {
|
||||
worldRateLimiter.update();
|
||||
}
|
||||
rateLimitUpdateCounter = 0;
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
logger.error("Error in update loop!");
|
||||
logger.error(t);
|
||||
}
|
||||
Util.sleep(50l);
|
||||
Util.sleep(100l);
|
||||
}
|
||||
}), "Relay Tick");
|
||||
tickThread.setDaemon(true);
|
||||
|
@ -114,6 +143,7 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
private static final Map<WebSocket,EaglerSPClient> clientConnections = new HashMap();
|
||||
private static final Map<String,EaglerSPServer> serverCodes = new HashMap();
|
||||
private static final Map<WebSocket,EaglerSPServer> serverConnections = new HashMap();
|
||||
private static final Map<String,List<EaglerSPClient>> clientAddressSets = new HashMap();
|
||||
private static final Map<String,List<EaglerSPServer>> serverAddressSets = new HashMap();
|
||||
|
||||
@Override
|
||||
|
@ -131,7 +161,32 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
}else {
|
||||
addr = arg0.getRemoteSocketAddress().getAddress().getHostAddress().toLowerCase();
|
||||
}
|
||||
|
||||
int totalCons = 0;
|
||||
synchronized(pendingConnections) {
|
||||
Iterator<PendingConnection> pendingItr = pendingConnections.values().iterator();
|
||||
while(pendingItr.hasNext()) {
|
||||
if(pendingItr.next().address.equals(addr)) {
|
||||
++totalCons;
|
||||
}
|
||||
}
|
||||
}
|
||||
synchronized(clientAddressSets) {
|
||||
List<EaglerSPClient> lst = clientAddressSets.get(addr);
|
||||
if(lst != null) {
|
||||
totalCons += lst.size();
|
||||
}
|
||||
}
|
||||
|
||||
if(totalCons >= config.getConnectionsPerIP()) {
|
||||
logger.debug("[{}]: Too many connections are open on this address", arg0.getAttachment());
|
||||
arg0.send(IPacketFEDisconnectClient.ratelimitPacketTooMany);
|
||||
arg0.close();
|
||||
return;
|
||||
}
|
||||
|
||||
arg0.setAttachment(addr);
|
||||
|
||||
PendingConnection waiting = new PendingConnection(millis, addr);
|
||||
logger.debug("[{}]: Connection opened", arg0.getRemoteSocketAddress());
|
||||
synchronized(pendingConnections) {
|
||||
|
@ -165,6 +220,21 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
return;
|
||||
}
|
||||
if(ipkt.connectionType == 0x01) {
|
||||
if(!rateLimit(worldRateLimiter, arg0, waiting.address)) {
|
||||
logger.debug("[{}]: Got world ratelimited", arg0.getAttachment());
|
||||
return;
|
||||
}
|
||||
synchronized(serverAddressSets) {
|
||||
List<EaglerSPServer> lst = serverAddressSets.get(waiting.address);
|
||||
if(lst != null) {
|
||||
if(lst.size() >= config.getWorldsPerIP()) {
|
||||
logger.debug("[{}]: Too many worlds are open on this address", arg0.getAttachment());
|
||||
arg0.send(IPacketFEDisconnectClient.ratelimitPacketTooMany);
|
||||
arg0.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.debug("[{}]: Connected as a server", arg0.getAttachment());
|
||||
EaglerSPServer srv;
|
||||
synchronized(serverCodes) {
|
||||
|
@ -200,7 +270,12 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
}
|
||||
srv.send(new IPacket01ICEServers(EaglerSPRelayConfigRelayList.relayServers));
|
||||
logger.debug("[{}][Relay -> Server] PKT 0x01: Send ICE server list to server", arg0.getAttachment());
|
||||
}else if(ipkt.connectionType == 0x02) {
|
||||
}else {
|
||||
if(!rateLimit(pingRateLimiter, arg0, waiting.address)) {
|
||||
logger.debug("[{}]: Got ping ratelimited", arg0.getAttachment());
|
||||
return;
|
||||
}
|
||||
if(ipkt.connectionType == 0x02) {
|
||||
String code = ipkt.connectionCode;
|
||||
logger.debug("[{}]: Connected as a client, requested server code: {}", arg0.getAttachment(), code);
|
||||
if(code.length() != config.getCodeLength()) {
|
||||
|
@ -226,7 +301,7 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
do {
|
||||
id = EaglerSPClient.generateClientId();
|
||||
}while(clientIds.containsKey(id));
|
||||
cl = new EaglerSPClient(arg0, srv, id);
|
||||
cl = new EaglerSPClient(arg0, srv, id, waiting.address);
|
||||
clientIds.put(id, cl);
|
||||
ipkt.connectionCode = id;
|
||||
arg0.send(IPacket.writePacket(ipkt));
|
||||
|
@ -235,6 +310,14 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
synchronized(clientConnections) {
|
||||
clientConnections.put(arg0, cl);
|
||||
}
|
||||
synchronized(clientAddressSets) {
|
||||
List<EaglerSPClient> lst = clientAddressSets.get(cl.address);
|
||||
if(lst == null) {
|
||||
lst = new ArrayList();
|
||||
clientAddressSets.put(cl.address, lst);
|
||||
}
|
||||
lst.add(cl);
|
||||
}
|
||||
cl.send(new IPacket01ICEServers(EaglerSPRelayConfigRelayList.relayServers));
|
||||
logger.debug("[{}][Relay -> Client] PKT 0x01: Send ICE server list to client", arg0.getAttachment());
|
||||
}
|
||||
|
@ -252,6 +335,7 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
"Unexpected Init Packet")));
|
||||
arg0.close();
|
||||
}
|
||||
}
|
||||
}else {
|
||||
logger.debug("[{}]: Pending connection did not send a 0x00 packet to identify "
|
||||
+ "as a client or server", arg0.getAttachment());
|
||||
|
@ -314,6 +398,15 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
synchronized(serverCodes) {
|
||||
serverCodes.remove(srv.code);
|
||||
}
|
||||
synchronized(serverAddressSets) {
|
||||
List<EaglerSPServer> lst = serverAddressSets.get(srv.serverAddress);
|
||||
if(lst != null) {
|
||||
lst.remove(srv);
|
||||
if(lst.size() == 0) {
|
||||
serverAddressSets.remove(srv.serverAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
ArrayList<EaglerSPClient> clientList;
|
||||
synchronized(clientConnections) {
|
||||
clientList = new ArrayList(clientConnections.values());
|
||||
|
@ -326,18 +419,20 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
cl.socket.close();
|
||||
}
|
||||
}
|
||||
synchronized(serverAddressSets) {
|
||||
List<EaglerSPServer> lst = serverAddressSets.get(srv.serverAddress);
|
||||
lst.remove(srv);
|
||||
if(lst.size() == 0) {
|
||||
serverAddressSets.remove(srv.serverAddress);
|
||||
}
|
||||
}
|
||||
}else {
|
||||
EaglerSPClient cl;
|
||||
synchronized(clientConnections) {
|
||||
cl = clientConnections.remove(arg0);
|
||||
}
|
||||
synchronized(clientAddressSets) {
|
||||
List<EaglerSPClient> lst = clientAddressSets.get(cl.address);
|
||||
if(lst != null) {
|
||||
lst.remove(cl);
|
||||
if(lst.size() == 0) {
|
||||
clientAddressSets.remove(cl.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(cl != null) {
|
||||
logger.debug("[{}]: Client closed, id: {}", arg0.getAttachment(), cl.id);
|
||||
synchronized(clientIds) {
|
||||
|
@ -376,4 +471,29 @@ public class EaglerSPRelay extends WebSocketServer {
|
|||
return lst;
|
||||
}
|
||||
|
||||
private boolean rateLimit(RateLimiter limiter, WebSocket sock, String addr) {
|
||||
if(limiter != null) {
|
||||
RateLimit l = limiter.limit(addr);
|
||||
if(l == RateLimit.NONE) {
|
||||
return true;
|
||||
}else if(l == RateLimit.LIMIT) {
|
||||
sock.send(IPacketFEDisconnectClient.ratelimitPacketBlock);
|
||||
sock.close();
|
||||
return false;
|
||||
}else if(l == RateLimit.LIMIT_NOW_LOCKOUT) {
|
||||
sock.send(IPacketFEDisconnectClient.ratelimitPacketBlockLock);
|
||||
sock.close();
|
||||
return false;
|
||||
}else if(l == RateLimit.LOCKOUT) {
|
||||
sock.send(IPacketFEDisconnectClient.ratelimitPacketLocked);
|
||||
sock.close();
|
||||
return false;
|
||||
}else {
|
||||
return true; // ?
|
||||
}
|
||||
}else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,12 +17,22 @@ public class EaglerSPRelayConfig {
|
|||
private int codeLength = 5;
|
||||
private String codeChars = "abcdefghijklmnopqrstuvwxyz0123456789$%&*+?!";
|
||||
private boolean codeMixCase = false;
|
||||
private int connectionsPerIP = 256;
|
||||
private boolean rateLimitEnable = false;
|
||||
private int rateLimitPeriod = 128;
|
||||
private int rateLimitLimit = 48;
|
||||
private int rateLimitLockoutLimit = 64;
|
||||
private int rateLimitLockoutDuration = 600;
|
||||
|
||||
private int connectionsPerIP = 128;
|
||||
private int worldsPerIP = 32;
|
||||
|
||||
private boolean openRateLimitEnable = true;
|
||||
private int openRateLimitPeriod = 192;
|
||||
private int openRateLimitLimit = 32;
|
||||
private int openRateLimitLockoutLimit = 48;
|
||||
private int openRateLimitLockoutDuration = 600;
|
||||
|
||||
private boolean pingRateLimitEnable = true;
|
||||
private int pingRateLimitPeriod = 256;
|
||||
private int pingRateLimitLimit = 128;
|
||||
private int pingRateLimitLockoutLimit = 192;
|
||||
private int pingRateLimitLockoutDuration = 300;
|
||||
|
||||
private String originWhitelist = "";
|
||||
private String[] originWhitelistArray = new String[0];
|
||||
private boolean enableRealIpHeader = false;
|
||||
|
@ -35,11 +45,17 @@ public class EaglerSPRelayConfig {
|
|||
}else {
|
||||
EaglerSPRelay.logger.info("Loading config file: {}", conf.getAbsoluteFile());
|
||||
boolean gotPort = false, gotCodeLength = false, gotCodeChars = false;
|
||||
boolean gotCodeMixCase = false, gotConnectionsPerIP = false;
|
||||
boolean gotRateLimitEnable = false, gotRateLimitPeriod = false;
|
||||
boolean gotRateLimitLimit = false, gotRateLimitLockoutLimit = false;
|
||||
boolean gotRateLimitLockoutDuration = false, gotOriginWhitelist = false;
|
||||
boolean gotEnableRealIpHeader = false, gotAddress = false, gotComment = false;
|
||||
boolean gotCodeMixCase = false;
|
||||
boolean gotConnectionsPerIP = false, gotWorldsPerIP = false,
|
||||
gotOpenRateLimitEnable = false, gotOpenRateLimitPeriod = false,
|
||||
gotOpenRateLimitLimit = false, gotOpenRateLimitLockoutLimit = false,
|
||||
gotOpenRateLimitLockoutDuration = false;
|
||||
boolean gotPingRateLimitEnable = false, gotPingRateLimitPeriod = false,
|
||||
gotPingRateLimitLimit = false, gotPingRateLimitLockoutLimit = false,
|
||||
gotPingRateLimitLockoutDuration = false;
|
||||
boolean gotOriginWhitelist = false, gotEnableRealIpHeader = false,
|
||||
gotAddress = false, gotComment = false;
|
||||
|
||||
Throwable t2 = null;
|
||||
try(BufferedReader reader = new BufferedReader(new FileReader(conf))) {
|
||||
String s;
|
||||
|
@ -86,6 +102,66 @@ public class EaglerSPRelayConfig {
|
|||
t2 = t;
|
||||
break;
|
||||
}
|
||||
}else if(ss[0].equalsIgnoreCase("worlds-per-ip")) {
|
||||
try {
|
||||
worldsPerIP = Integer.parseInt(ss[1]);
|
||||
gotWorldsPerIP = true;
|
||||
}catch(Throwable t) {
|
||||
EaglerSPRelay.logger.warn("Invalid worlds-per-ip {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn(t);
|
||||
t2 = t;
|
||||
break;
|
||||
}
|
||||
}else if(ss[0].equalsIgnoreCase("world-ratelimit-enable")) {
|
||||
try {
|
||||
openRateLimitEnable = getBooleanValue(ss[1]);
|
||||
gotOpenRateLimitEnable = true;
|
||||
}catch(Throwable t) {
|
||||
EaglerSPRelay.logger.warn("Invalid world-ratelimit-enable {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn(t);
|
||||
t2 = t;
|
||||
break;
|
||||
}
|
||||
}else if(ss[0].equalsIgnoreCase("world-ratelimit-period")) {
|
||||
try {
|
||||
openRateLimitPeriod = Integer.parseInt(ss[1]);
|
||||
gotOpenRateLimitPeriod = true;
|
||||
}catch(Throwable t) {
|
||||
EaglerSPRelay.logger.warn("Invalid world-ratelimit-period {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn(t);
|
||||
t2 = t;
|
||||
break;
|
||||
}
|
||||
}else if(ss[0].equalsIgnoreCase("world-ratelimit-limit")) {
|
||||
try {
|
||||
openRateLimitLimit = Integer.parseInt(ss[1]);
|
||||
gotOpenRateLimitLimit = true;
|
||||
}catch(Throwable t) {
|
||||
EaglerSPRelay.logger.warn("Invalid world-ratelimit-limit {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn(t);
|
||||
t2 = t;
|
||||
break;
|
||||
}
|
||||
}else if(ss[0].equalsIgnoreCase("world-ratelimit-lockout-limit")) {
|
||||
try {
|
||||
openRateLimitLockoutLimit = Integer.parseInt(ss[1]);
|
||||
gotOpenRateLimitLockoutLimit = true;
|
||||
}catch(Throwable t) {
|
||||
EaglerSPRelay.logger.warn("Invalid world-ratelimit-lockout-limit {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn(t);
|
||||
t2 = t;
|
||||
break;
|
||||
}
|
||||
}else if(ss[0].equalsIgnoreCase("world-ratelimit-lockout-duration")) {
|
||||
try {
|
||||
openRateLimitLockoutDuration = Integer.parseInt(ss[1]);
|
||||
gotOpenRateLimitLockoutDuration = true;
|
||||
}catch(Throwable t) {
|
||||
EaglerSPRelay.logger.warn("Invalid world-ratelimit-lockout-duration {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn(t);
|
||||
t2 = t;
|
||||
break;
|
||||
}
|
||||
}else if(ss[0].equalsIgnoreCase("connections-per-ip")) {
|
||||
try {
|
||||
connectionsPerIP = Integer.parseInt(ss[1]);
|
||||
|
@ -96,52 +172,52 @@ public class EaglerSPRelayConfig {
|
|||
t2 = t;
|
||||
break;
|
||||
}
|
||||
}else if(ss[0].equalsIgnoreCase("ratelimit-enable")) {
|
||||
}else if(ss[0].equalsIgnoreCase("ping-ratelimit-enable")) {
|
||||
try {
|
||||
rateLimitEnable = getBooleanValue(ss[1]);
|
||||
gotRateLimitEnable = true;
|
||||
pingRateLimitEnable = getBooleanValue(ss[1]);
|
||||
gotPingRateLimitEnable = true;
|
||||
}catch(Throwable t) {
|
||||
EaglerSPRelay.logger.warn("Invalid rate-limit-enable {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn("Invalid ping-ratelimit-enable {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn(t);
|
||||
t2 = t;
|
||||
break;
|
||||
}
|
||||
}else if(ss[0].equalsIgnoreCase("ratelimit-period")) {
|
||||
}else if(ss[0].equalsIgnoreCase("ping-ratelimit-period")) {
|
||||
try {
|
||||
rateLimitPeriod = Integer.parseInt(ss[1]);
|
||||
gotRateLimitPeriod = true;
|
||||
pingRateLimitPeriod = Integer.parseInt(ss[1]);
|
||||
gotPingRateLimitPeriod = true;
|
||||
}catch(Throwable t) {
|
||||
EaglerSPRelay.logger.warn("Invalid ratelimit-period {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn("Invalid ping-ratelimit-period {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn(t);
|
||||
t2 = t;
|
||||
break;
|
||||
}
|
||||
}else if(ss[0].equalsIgnoreCase("ratelimit-limit")) {
|
||||
}else if(ss[0].equalsIgnoreCase("ping-ratelimit-limit")) {
|
||||
try {
|
||||
rateLimitLimit = Integer.parseInt(ss[1]);
|
||||
gotRateLimitLimit = true;
|
||||
pingRateLimitLimit = Integer.parseInt(ss[1]);
|
||||
gotPingRateLimitLimit = true;
|
||||
}catch(Throwable t) {
|
||||
EaglerSPRelay.logger.warn("Invalid ratelimit-limit {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn("Invalid ping-ratelimit-limit {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn(t);
|
||||
t2 = t;
|
||||
break;
|
||||
}
|
||||
}else if(ss[0].equalsIgnoreCase("ratelimit-lockout-limit")) {
|
||||
}else if(ss[0].equalsIgnoreCase("ping-ratelimit-lockout-limit")) {
|
||||
try {
|
||||
rateLimitLockoutLimit = Integer.parseInt(ss[1]);
|
||||
gotRateLimitLockoutLimit = true;
|
||||
pingRateLimitLockoutLimit = Integer.parseInt(ss[1]);
|
||||
gotPingRateLimitLockoutLimit = true;
|
||||
}catch(Throwable t) {
|
||||
EaglerSPRelay.logger.warn("Invalid ratelimit-lockout-limit {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn("Invalid ping-ratelimit-lockout-limit {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn(t);
|
||||
t2 = t;
|
||||
break;
|
||||
}
|
||||
}else if(ss[0].equalsIgnoreCase("ratelimit-lockout-duration")) {
|
||||
}else if(ss[0].equalsIgnoreCase("ping-ratelimit-lockout-duration")) {
|
||||
try {
|
||||
rateLimitLockoutDuration = Integer.parseInt(ss[1]);
|
||||
gotRateLimitLockoutDuration = true;
|
||||
pingRateLimitLockoutDuration = Integer.parseInt(ss[1]);
|
||||
gotPingRateLimitLockoutDuration = true;
|
||||
}catch(Throwable t) {
|
||||
EaglerSPRelay.logger.warn("Invalid ratelimit-lockout-duration {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn("Invalid ping-ratelimit-lockout-duration {} in conf {}", ss[1], conf.getAbsoluteFile());
|
||||
EaglerSPRelay.logger.warn(t);
|
||||
t2 = t;
|
||||
break;
|
||||
|
@ -174,10 +250,14 @@ public class EaglerSPRelayConfig {
|
|||
t2 = t;
|
||||
}
|
||||
if(t2 != null || !gotPort || !gotCodeLength || !gotCodeChars ||
|
||||
!gotCodeMixCase || !gotConnectionsPerIP || !gotRateLimitEnable ||
|
||||
!gotRateLimitPeriod || !gotRateLimitLimit || !gotRateLimitLockoutLimit ||
|
||||
!gotRateLimitLockoutDuration || !gotOriginWhitelist ||
|
||||
!gotEnableRealIpHeader || !gotAddress || !gotComment) {
|
||||
!gotCodeMixCase || !gotWorldsPerIP || !gotOpenRateLimitEnable ||
|
||||
!gotOpenRateLimitPeriod || !gotOpenRateLimitLimit ||
|
||||
!gotOpenRateLimitLockoutLimit || !gotOpenRateLimitLockoutDuration ||
|
||||
!gotConnectionsPerIP || !gotPingRateLimitEnable ||
|
||||
!gotPingRateLimitPeriod || !gotPingRateLimitLimit ||
|
||||
!gotPingRateLimitLockoutLimit || !gotPingRateLimitLockoutDuration ||
|
||||
!gotOriginWhitelist || !gotEnableRealIpHeader || !gotAddress ||
|
||||
!gotComment) {
|
||||
EaglerSPRelay.logger.warn("Updating config file: {}", conf.getAbsoluteFile());
|
||||
save(conf);
|
||||
}
|
||||
|
@ -204,11 +284,17 @@ public class EaglerSPRelayConfig {
|
|||
w.println("code-chars=" + codeChars);
|
||||
w.println("code-mix-case=" + codeMixCase);
|
||||
w.println("connections-per-ip=" + connectionsPerIP);
|
||||
w.println("ratelimit-enable=" + rateLimitEnable);
|
||||
w.println("ratelimit-period=" + rateLimitPeriod);
|
||||
w.println("ratelimit-limit=" + rateLimitLimit);
|
||||
w.println("ratelimit-lockout-limit=" + rateLimitLockoutLimit);
|
||||
w.println("ratelimit-lockout-duration=" + rateLimitLockoutDuration);
|
||||
w.println("ping-ratelimit-enable=" + pingRateLimitEnable);
|
||||
w.println("ping-ratelimit-period=" + pingRateLimitPeriod);
|
||||
w.println("ping-ratelimit-limit=" + pingRateLimitLimit);
|
||||
w.println("ping-ratelimit-lockout-limit=" + pingRateLimitLockoutLimit);
|
||||
w.println("ping-ratelimit-lockout-duration=" + pingRateLimitLockoutDuration);
|
||||
w.println("worlds-per-ip=" + worldsPerIP);
|
||||
w.println("world-ratelimit-enable=" + openRateLimitEnable);
|
||||
w.println("world-ratelimit-period=" + openRateLimitPeriod);
|
||||
w.println("world-ratelimit-limit=" + openRateLimitLimit);
|
||||
w.println("world-ratelimit-lockout-limit=" + openRateLimitLockoutLimit);
|
||||
w.println("world-ratelimit-lockout-duration=" + openRateLimitLockoutDuration);
|
||||
w.println("origin-whitelist=" + originWhitelist);
|
||||
w.println("enable-real-ip-header=" + enableRealIpHeader);
|
||||
w.print("server-comment=" + serverComment);
|
||||
|
@ -252,24 +338,48 @@ public class EaglerSPRelayConfig {
|
|||
return connectionsPerIP;
|
||||
}
|
||||
|
||||
public boolean isRateLimitEnable() {
|
||||
return rateLimitEnable;
|
||||
public boolean isPingRateLimitEnable() {
|
||||
return pingRateLimitEnable;
|
||||
}
|
||||
|
||||
public int getRateLimitPeriod() {
|
||||
return rateLimitPeriod;
|
||||
public int getPingRateLimitPeriod() {
|
||||
return pingRateLimitPeriod;
|
||||
}
|
||||
|
||||
public int getRateLimitLimit() {
|
||||
return rateLimitLimit;
|
||||
public int getPingRateLimitLimit() {
|
||||
return pingRateLimitLimit;
|
||||
}
|
||||
|
||||
public int getRateLimitLockoutLimit() {
|
||||
return rateLimitLockoutLimit;
|
||||
public int getPingRateLimitLockoutLimit() {
|
||||
return pingRateLimitLockoutLimit;
|
||||
}
|
||||
|
||||
public int getRateLimitLockoutDuration() {
|
||||
return rateLimitLockoutDuration;
|
||||
public int getPingRateLimitLockoutDuration() {
|
||||
return pingRateLimitLockoutDuration;
|
||||
}
|
||||
|
||||
public int getWorldsPerIP() {
|
||||
return worldsPerIP;
|
||||
}
|
||||
|
||||
public boolean isWorldRateLimitEnable() {
|
||||
return openRateLimitEnable;
|
||||
}
|
||||
|
||||
public int getWorldRateLimitPeriod() {
|
||||
return openRateLimitPeriod;
|
||||
}
|
||||
|
||||
public int getWorldRateLimitLimit() {
|
||||
return openRateLimitLimit;
|
||||
}
|
||||
|
||||
public int getWorldRateLimitLockoutLimit() {
|
||||
return openRateLimitLockoutLimit;
|
||||
}
|
||||
|
||||
public int getWorldRateLimitLockoutDuration() {
|
||||
return openRateLimitLockoutDuration;
|
||||
}
|
||||
|
||||
public String getOriginWhitelist() {
|
||||
|
@ -280,6 +390,23 @@ public class EaglerSPRelayConfig {
|
|||
return originWhitelistArray;
|
||||
}
|
||||
|
||||
public boolean getIsWhitelisted(String domain) {
|
||||
domain = domain.toLowerCase();
|
||||
for(int i = 0; i < originWhitelistArray.length; ++i) {
|
||||
String etr = originWhitelistArray[i].toLowerCase();
|
||||
if(etr.startsWith("*")) {
|
||||
if(domain.endsWith(etr.substring(1))) {
|
||||
return true;
|
||||
}
|
||||
}else {
|
||||
if(domain.equals(etr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isEnableRealIpHeader() {
|
||||
return enableRealIpHeader;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
package net.lax1dude.eaglercraft.sp.relay;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public class RateLimiter {
|
||||
|
||||
private final int period;
|
||||
private final int limit;
|
||||
private final int lockoutLimit;
|
||||
private final int lockoutDuration;
|
||||
|
||||
private class RateLimitEntry {
|
||||
|
||||
protected long timer;
|
||||
protected int count;
|
||||
protected long lockedTimer;
|
||||
protected boolean locked;
|
||||
|
||||
protected RateLimitEntry() {
|
||||
timer = System.currentTimeMillis();
|
||||
count = 0;
|
||||
lockedTimer = 0l;
|
||||
locked = false;
|
||||
}
|
||||
|
||||
protected void update() {
|
||||
long millis = System.currentTimeMillis();
|
||||
if(locked) {
|
||||
if(millis - lockedTimer > RateLimiter.this.lockoutDuration) {
|
||||
timer = millis;
|
||||
count = 0;
|
||||
lockedTimer = 0l;
|
||||
locked = false;
|
||||
}
|
||||
}else {
|
||||
long p = RateLimiter.this.period;
|
||||
int breaker = 0;
|
||||
while(millis - timer > p) {
|
||||
timer += p;
|
||||
--count;
|
||||
if(count < 0 || ++breaker > 100) {
|
||||
timer = millis;
|
||||
count = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static enum RateLimit {
|
||||
NONE, LIMIT, LIMIT_NOW_LOCKOUT, LOCKOUT;
|
||||
}
|
||||
|
||||
private final Map<String, RateLimitEntry> limiters = new HashMap();
|
||||
|
||||
public RateLimiter(int period, int limit, int lockoutLimit, int lockoutDuration) {
|
||||
this.period = period;
|
||||
this.limit = limit;
|
||||
this.lockoutLimit = lockoutLimit;
|
||||
this.lockoutDuration = lockoutDuration;
|
||||
}
|
||||
|
||||
public RateLimit limit(String addr) {
|
||||
synchronized(this) {
|
||||
RateLimitEntry etr = limiters.get(addr);
|
||||
|
||||
if(etr == null) {
|
||||
etr = new RateLimitEntry();
|
||||
limiters.put(addr, etr);
|
||||
}else {
|
||||
etr.update();
|
||||
}
|
||||
|
||||
if(etr.locked) {
|
||||
return RateLimit.LOCKOUT;
|
||||
}
|
||||
|
||||
++etr.count;
|
||||
if(etr.count >= lockoutLimit) {
|
||||
etr.count = 0;
|
||||
etr.locked = true;
|
||||
etr.lockedTimer = System.currentTimeMillis();
|
||||
return RateLimit.LIMIT_NOW_LOCKOUT;
|
||||
}else if(etr.count > limit) {
|
||||
return RateLimit.LIMIT;
|
||||
}else {
|
||||
return RateLimit.NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void update() {
|
||||
synchronized(this) {
|
||||
Iterator<RateLimitEntry> itr = limiters.values().iterator();
|
||||
while(itr.hasNext()) {
|
||||
if(itr.next().count == 0) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
synchronized(this) {
|
||||
limiters.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,7 @@ package net.lax1dude.eaglercraft.sp.relay.pkt;
|
|||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class IPacketFEDisconnectClient extends IPacket {
|
||||
|
||||
|
@ -42,4 +43,9 @@ public class IPacketFEDisconnectClient extends IPacket {
|
|||
return -1;
|
||||
}
|
||||
|
||||
public static final ByteBuffer ratelimitPacketTooMany = ByteBuffer.wrap(new byte[] { (byte)0xFC, (byte)0x00 });
|
||||
public static final ByteBuffer ratelimitPacketBlock = ByteBuffer.wrap(new byte[] { (byte)0xFC, (byte)0x01 });
|
||||
public static final ByteBuffer ratelimitPacketBlockLock = ByteBuffer.wrap(new byte[] { (byte)0xFC, (byte)0x02 });
|
||||
public static final ByteBuffer ratelimitPacketLocked = ByteBuffer.wrap(new byte[] { (byte)0xFC, (byte)0x03 });
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user