diff --git a/LaunchServer/resources/launchserver/defaults/config.cfg b/LaunchServer/resources/launchserver/defaults/config.cfg index 7ee685d..d7f5dfa 100644 --- a/LaunchServer/resources/launchserver/defaults/config.cfg +++ b/LaunchServer/resources/launchserver/defaults/config.cfg @@ -12,9 +12,16 @@ ]; # Лимит на авторизацию / защита от брутфорса -authRateLimit: 5; -authRateLimitMilis: 8000; -authRejectString: "Превышен лимит авторизаций, подождите некоторое время!"; +authLimit: false; +authLimitConfig: { + authRateLimit: 5; + authRateLimitMilis: 8000; + authRejectString: "Превышен лимит авторизаций, подождите некоторое время!"; + authBannedString: "Ваш IP заблокирован!"; + blockOnConnect: false; + allowIp: []; + blockIp: []; +}; # Управление системой хранения и обработки UUID пользователей authHandler: "textFile"; @@ -35,12 +42,12 @@ # Конфигурация не требуется для текстур Mojang }; -# Название файла вашего лаунчера +# Название файла вашего лаунчера (jar и exe) binaryName: "Launcher"; # Собрать EXE файл (с помощью Launch4J) -launch4J: { - enabled: false; +launch4J: false; +launch4JConfig: { productName: "LauncherSchool"; fileDesc: "LauncherSchool by KeeperJerry"; internalName: "Launcher"; diff --git a/LaunchServer/source/LaunchServer.java b/LaunchServer/source/LaunchServer.java index 57bbb0d..6fb7075 100644 --- a/LaunchServer/source/LaunchServer.java +++ b/LaunchServer/source/LaunchServer.java @@ -13,17 +13,15 @@ import launcher.serialize.config.entry.*; import launcher.serialize.signed.SignedObjectHolder; import launchserver.auth.AuthException; -import launchserver.auth.AuthLimiter; +import launchserver.auth.limiter.AuthLimiter; import launchserver.auth.MySQLSourceConfig; import launchserver.auth.handler.AuthHandler; import launchserver.auth.handler.CachedAuthHandler; import launchserver.auth.handler.FileAuthHandler; +import launchserver.auth.limiter.AuthLimiterConfig; import launchserver.auth.provider.AuthProvider; import launchserver.auth.provider.DigestAuthProvider; -import launchserver.binary.EXEL4JLauncherBinary; -import launchserver.binary.EXELauncherBinary; -import launchserver.binary.JARLauncherBinary; -import launchserver.binary.LauncherBinary; +import launchserver.binary.*; import launchserver.command.Command; import launchserver.command.CommandException; import launchserver.command.handler.CommandHandler; @@ -367,7 +365,7 @@ } private LauncherBinary binary() { - if (config.launch4J.enabled) return new EXEL4JLauncherBinary(this); + if (config.launch4J) return new EXEL4JLauncherBinary(this); return new EXELauncherBinary(this); } @@ -554,11 +552,9 @@ // AuthLimiter @LauncherAPI - public final int authRateLimit; + public final Boolean authLimit; @LauncherAPI - public final int authRateLimitMilis; - @LauncherAPI - public final String authRejectString; + public final AuthLimiterConfig authLimitConfig; // Mirrors @LauncherAPI @@ -574,7 +570,9 @@ // Misc options @LauncherAPI - public final ExeConf launch4J; + public final boolean launch4J; + @LauncherAPI + public final EXEL4JLauncherConfig launch4JConfig; @LauncherAPI public final boolean compress; private final StringConfigEntry address; @@ -590,12 +588,8 @@ block.getEntryValue("bindAddress", StringConfigEntry.class) : getAddress(); // Limit Autorization - authRateLimit = VerifyHelper.verifyInt(block.getEntryValue("authRateLimit", IntegerConfigEntry.class), - VerifyHelper.range(0, 1000000), "Illegal authRateLimit"); - authRateLimitMilis = VerifyHelper.verifyInt(block.getEntryValue("authRateLimitMilis", IntegerConfigEntry.class), - VerifyHelper.range(10, 10000000), "Illegal authRateLimitMillis"); - authRejectString = block.hasEntry("authRejectString") ? - block.getEntryValue("authRejectString", StringConfigEntry.class) : "Превышен лимит авторизаций. Подождите некоторое время перед повторной попыткой"; + authLimit = block.getEntryValue("authLimit", BooleanConfigEntry.class); + authLimitConfig = new AuthLimiterConfig(block.getEntry("authLimitConfig", BlockConfigEntry.class)); // Set handlers & providers authHandler = AuthHandler.newHandler(block.getEntryValue("authHandler", StringConfigEntry.class), @@ -612,36 +606,13 @@ mirrors = block.getEntry("mirrors", ListConfigEntry.class); // Set misc config - launch4J = new ExeConf(block.getEntry("launch4J", BlockConfigEntry.class)); + launch4J = block.getEntryValue("launch4J", BooleanConfigEntry.class); + launch4JConfig = new EXEL4JLauncherConfig(block.getEntry("launch4JConfig", BlockConfigEntry.class)); + binaryName = block.getEntryValue("binaryName", StringConfigEntry.class); compress = block.getEntryValue("compress", BooleanConfigEntry.class); } - public static class ExeConf extends ConfigObject { - public final boolean enabled; - public String productName; - public String fileDesc; - public String internalName; - public String copyright; - public String trademarks; - - private ExeConf(BlockConfigEntry block) - { - super(block); - enabled = block.getEntryValue("enabled", BooleanConfigEntry.class); - productName = block.hasEntry("productName") ? block.getEntryValue("productName", StringConfigEntry.class) - : "LauncherSchool"; - fileDesc = block.hasEntry("fileDesc") ? block.getEntryValue("fileDesc", StringConfigEntry.class) - : "LauncherSchool by KeeperJerry"; - internalName = block.hasEntry("internalName") ? block.getEntryValue("internalName", StringConfigEntry.class) - : "Launcher"; - copyright = block.hasEntry("copyright") ? block.getEntryValue("copyright", StringConfigEntry.class) - : "© KeeperJerry"; - trademarks = block.hasEntry("trademarks") ? block.getEntryValue("trademarks", StringConfigEntry.class) - : "This product is licensed under GNU v3.0"; - } - } - @LauncherAPI public String getAddress() { diff --git a/LaunchServer/source/auth/AuthLimiter.java b/LaunchServer/source/auth/AuthLimiter.java deleted file mode 100644 index 7488829..0000000 --- a/LaunchServer/source/auth/AuthLimiter.java +++ /dev/null @@ -1,100 +0,0 @@ -package launchserver.auth; - -import launcher.LauncherAPI; -import launchserver.LaunchServer; - -import java.util.HashMap; - -public class AuthLimiter -{ - @LauncherAPI - public static final long TIMEOUT = 10 * 60 * 1000; //10 минут - public final int rateLimit; - public final int rateLimitMilis; - private final HashMap map; - - public AuthLimiter(LaunchServer srv) - { - map = new HashMap<>(); - rateLimit = srv.config.authRateLimit; - rateLimitMilis = srv.config.authRateLimitMilis; - } - - public boolean isLimit(String ip) - { - if (map.containsKey(ip)) - { - AuthEntry rate = map.get(ip); - long currenttime = System.currentTimeMillis(); - if (rate.ts + rateLimitMilis < currenttime) - { - rate.value = 0; - } - if (rate.value >= rateLimit && rateLimit > 0) - { - rate.value++; - rate.ts = currenttime; - return true; - } - rate.value++; - rate.ts = currenttime; - return false; - } - map.put(ip, new AuthEntry(1, System.currentTimeMillis())); - return false; - } - - static class AuthEntry - { - public int value; - public long ts; - - public AuthEntry(int i, long l) - { - value = i; - ts = l; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - - if (!(obj instanceof AuthEntry)) - { - return false; - } - - AuthEntry other = (AuthEntry) obj; - if (ts != other.ts) - { - return false; - } - - return value == other.value; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + (int) (ts ^ ts >>> 32); - result = prime * result + value; - return result; - } - - @Override - public String toString() - { - return String.format("AuthEntry {value=%s, ts=%s}", value, ts); - } - } -} diff --git a/LaunchServer/source/auth/limiter/AuthEntry.java b/LaunchServer/source/auth/limiter/AuthEntry.java new file mode 100644 index 0000000..fd7793d --- /dev/null +++ b/LaunchServer/source/auth/limiter/AuthEntry.java @@ -0,0 +1,55 @@ +package launchserver.auth.limiter; + +public class AuthEntry +{ + public int value; + public long ts; + + public AuthEntry(int i, long l) + { + value = i; + ts = l; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + + if (!(obj instanceof AuthEntry)) + { + return false; + } + + AuthEntry other = (AuthEntry) obj; + if (ts != other.ts) + { + return false; + } + + return value == other.value; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + (int) (ts ^ ts >>> 32); + result = prime * result + value; + return result; + } + + @Override + public String toString() + { + return String.format("AuthEntry {value=%s, ts=%s}", value, ts); + } +} diff --git a/LaunchServer/source/auth/limiter/AuthLimiter.java b/LaunchServer/source/auth/limiter/AuthLimiter.java new file mode 100644 index 0000000..fc8ea04 --- /dev/null +++ b/LaunchServer/source/auth/limiter/AuthLimiter.java @@ -0,0 +1,46 @@ +package launchserver.auth.limiter; + +import launcher.LauncherAPI; +import launchserver.LaunchServer; + +import java.util.HashMap; + +public class AuthLimiter +{ + @LauncherAPI + public static final long TIMEOUT = 10 * 60 * 1000; //10 минут + public final int rateLimit; + public final int rateLimitMilis; + private final HashMap map; + + public AuthLimiter(LaunchServer srv) + { + map = new HashMap<>(); + rateLimit = srv.config.authLimitConfig.authRateLimit; + rateLimitMilis = srv.config.authLimitConfig.authRateLimitMilis; + } + + public boolean isLimit(String ip) + { + if (map.containsKey(ip)) + { + AuthEntry rate = map.get(ip); + long currenttime = System.currentTimeMillis(); + if (rate.ts + rateLimitMilis < currenttime) + { + rate.value = 0; + } + if (rate.value >= rateLimit && rateLimit > 0) + { + rate.value++; + rate.ts = currenttime; + return true; + } + rate.value++; + rate.ts = currenttime; + return false; + } + map.put(ip, new AuthEntry(1, System.currentTimeMillis())); + return false; + } +} diff --git a/LaunchServer/source/auth/limiter/AuthLimiterConfig.java b/LaunchServer/source/auth/limiter/AuthLimiterConfig.java new file mode 100644 index 0000000..0b82822 --- /dev/null +++ b/LaunchServer/source/auth/limiter/AuthLimiterConfig.java @@ -0,0 +1,44 @@ +package launchserver.auth.limiter; + +import launcher.LauncherAPI; +import launcher.helper.VerifyHelper; +import launcher.serialize.config.ConfigObject; +import launcher.serialize.config.entry.*; +import launchserver.LaunchServer; + +public class AuthLimiterConfig extends ConfigObject +{ + @LauncherAPI + public LaunchServer server; + @LauncherAPI + public int authRateLimit; + @LauncherAPI + public int authRateLimitMilis; + @LauncherAPI + public String authRejectString; + @LauncherAPI + public String authBannedString; + @LauncherAPI + public boolean blockOnConnect; + @LauncherAPI + public ListConfigEntry allowIp; + @LauncherAPI + public ListConfigEntry blockIp; + + @LauncherAPI + public AuthLimiterConfig(BlockConfigEntry block) + { + super(block); + authRateLimit = VerifyHelper.verifyInt(block.getEntryValue("authRateLimit", IntegerConfigEntry.class), + VerifyHelper.range(0, 1000000), "Illegal authRateLimit"); + authRateLimitMilis = VerifyHelper.verifyInt(block.getEntryValue("authRateLimitMilis", IntegerConfigEntry.class), + VerifyHelper.range(10, 10000000), "Illegal authRateLimitMillis"); + authRejectString = block.hasEntry("authRejectString") ? + block.getEntryValue("authRejectString", StringConfigEntry.class) : "Превышен лимит авторизаций. Подождите некоторое время перед повторной попыткой"; + authBannedString = block.hasEntry("authBannedString") ? + block.getEntryValue("authBannedString", StringConfigEntry.class) : "Ваш IP заблокирован!"; + blockOnConnect = block.getEntryValue("blockOnConnect", BooleanConfigEntry.class); + allowIp = block.getEntry("allowIp", ListConfigEntry.class); + blockIp = block.getEntry("blockIp", ListConfigEntry.class); + } +} diff --git a/LaunchServer/source/binary/EXEL4JLauncherBinary.java b/LaunchServer/source/binary/EXEL4JLauncherBinary.java index 6298c0c..fb22e22 100644 --- a/LaunchServer/source/binary/EXEL4JLauncherBinary.java +++ b/LaunchServer/source/binary/EXEL4JLauncherBinary.java @@ -82,20 +82,20 @@ // Prepare version info (product) VersionInfo info = new VersionInfo(); - info.setProductName(server.config.launch4J.productName); + info.setProductName(server.config.launch4JConfig.productName); info.setProductVersion(Launcher.VERSION); info.setTxtProductVersion(Launcher.VERSION + ", build " + Launcher.BUILD); // Prepare version info (file) - info.setFileDescription(server.config.launch4J.fileDesc); + info.setFileDescription(server.config.launch4JConfig.fileDesc); info.setFileVersion(Launcher.VERSION); info.setTxtFileVersion(Launcher.VERSION + ", build " + Launcher.BUILD); info.setOriginalFilename(binaryFile.getFileName().toString()); // Prepare version info (misc) - info.setInternalName(server.config.launch4J.internalName); - info.setCopyright(server.config.launch4J.copyright); - info.setTrademarks(server.config.launch4J.trademarks); + info.setInternalName(server.config.launch4JConfig.internalName); + info.setCopyright(server.config.launch4JConfig.copyright); + info.setTrademarks(server.config.launch4JConfig.trademarks); info.setLanguage(LanguageID.RUSSIAN); config.setVersionInfo(info); diff --git a/LaunchServer/source/binary/EXEL4JLauncherConfig.java b/LaunchServer/source/binary/EXEL4JLauncherConfig.java new file mode 100644 index 0000000..ffccd23 --- /dev/null +++ b/LaunchServer/source/binary/EXEL4JLauncherConfig.java @@ -0,0 +1,36 @@ +package launchserver.binary; + +import launcher.LauncherAPI; +import launcher.serialize.config.ConfigObject; +import launcher.serialize.config.entry.BlockConfigEntry; +import launcher.serialize.config.entry.StringConfigEntry; + +public class EXEL4JLauncherConfig extends ConfigObject +{ + @LauncherAPI + public String productName; + @LauncherAPI + public String fileDesc; + @LauncherAPI + public String internalName; + @LauncherAPI + public String copyright; + @LauncherAPI + public String trademarks; + + @LauncherAPI + public EXEL4JLauncherConfig(BlockConfigEntry block) + { + super(block); + productName = block.hasEntry("productName") ? block.getEntryValue("productName", StringConfigEntry.class) + : "LauncherSchool"; + fileDesc = block.hasEntry("fileDesc") ? block.getEntryValue("fileDesc", StringConfigEntry.class) + : "LauncherSchool by KeeperJerry"; + internalName = block.hasEntry("internalName") ? block.getEntryValue("internalName", StringConfigEntry.class) + : "Launcher"; + copyright = block.hasEntry("copyright") ? block.getEntryValue("copyright", StringConfigEntry.class) + : "© KeeperJerry"; + trademarks = block.hasEntry("trademarks") ? block.getEntryValue("trademarks", StringConfigEntry.class) + : "This product is licensed under GNU v3.0"; + } +} diff --git a/LaunchServer/source/response/ResponseThread.java b/LaunchServer/source/response/ResponseThread.java index d26de18..edd0349 100644 --- a/LaunchServer/source/response/ResponseThread.java +++ b/LaunchServer/source/response/ResponseThread.java @@ -9,6 +9,7 @@ import launcher.request.RequestException; import launcher.serialize.HInput; import launcher.serialize.HOutput; +import launcher.serialize.config.entry.StringConfigEntry; import launchserver.LaunchServer; import launchserver.response.auth.AuthResponse; import launchserver.response.auth.CheckServerResponse; @@ -44,9 +45,14 @@ @Override public void run() { - if (!server.serverSocketHandler.logConnections) + if (server.config.authLimitConfig.blockIp.stream(StringConfigEntry.class).anyMatch(s -> s.equals(ip)) && server.config.authLimitConfig.blockOnConnect) { - LogHelper.debug("Connection from %s", ip); + if (!server.serverSocketHandler.logConnections) LogHelper.debug("Blocked connection from %s", ip); + return; + } + else + { + if (!server.serverSocketHandler.logConnections) LogHelper.debug("Connection from %s", ip); } // Process connection diff --git a/LaunchServer/source/response/auth/AuthResponse.java b/LaunchServer/source/response/auth/AuthResponse.java index b15177c..ff113bd 100644 --- a/LaunchServer/source/response/auth/AuthResponse.java +++ b/LaunchServer/source/response/auth/AuthResponse.java @@ -6,6 +6,7 @@ import launcher.helper.VerifyHelper; import launcher.serialize.HInput; import launcher.serialize.HOutput; +import launcher.serialize.config.entry.StringConfigEntry; import launchserver.LaunchServer; import launchserver.auth.AuthException; import launchserver.auth.provider.AuthProvider; @@ -59,11 +60,21 @@ AuthProviderResult result; try { - if (server.limiter.isLimit(ip)) + if (server.config.authLimitConfig.blockIp.stream(StringConfigEntry.class).anyMatch(s -> s.equals(ip)) && !server.config.authLimitConfig.blockOnConnect) { - AuthProvider.authError(server.config.authRejectString); + AuthProvider.authError(server.config.authLimitConfig.authBannedString); return; } + + if (server.config.authLimitConfig.allowIp.stream(StringConfigEntry.class).noneMatch(s -> s.equals(ip))) + { + if (server.limiter.isLimit(ip)) + { + AuthProvider.authError(server.config.authLimitConfig.authRejectString); + return; + } + } + result = server.config.authProvider.auth(login, password, ip); if (!VerifyHelper.isValidUsername(result.username)) { diff --git a/buildnumber b/buildnumber index 458ed5d..86f4ab8 100644 --- a/buildnumber +++ b/buildnumber @@ -1 +1 @@ -545, 01.05.2021 \ No newline at end of file +546, 02.05.2021 \ No newline at end of file