mirror of
https://github.com/TuxCoding/FastLogin.git
synced 2025-07-29 18:27:36 +02:00
Add license
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java index ab4ceed..9c2fa3a 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java @@ -114,9 +114,9 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman } if (pluginManager.isPluginEnabled("ProtocolSupport")) { - pluginManager.registerEvents(new ProtocolSupportListener(this, core.getRateLimiter()), this); + pluginManager.registerEvents(new ProtocolSupportListener(this, core.getAntiBot()), this); } else if (pluginManager.isPluginEnabled("ProtocolLib")) { - ProtocolLibListener.register(this, core.getRateLimiter()); + ProtocolLibListener.register(this, core.getAntiBot()); if (isPluginInstalled("floodgate")) { if (getConfig().getBoolean("floodgatePrefixWorkaround")){ diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java index 8b3d601..3c161b7 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java @@ -31,8 +31,10 @@ import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; -import com.github.games647.fastlogin.core.RateLimiter; +import com.github.games647.fastlogin.core.antibot.AntiBotService; +import com.github.games647.fastlogin.core.antibot.AntiBotService.Action; +import java.net.InetSocketAddress; import java.security.KeyPair; import java.security.SecureRandom; @@ -50,9 +52,9 @@ public class ProtocolLibListener extends PacketAdapter { //just create a new once on plugin enable. This used for verify token generation private final SecureRandom random = new SecureRandom(); private final KeyPair keyPair = EncryptionUtil.generateKeyPair(); - private final RateLimiter rateLimiter; + private final AntiBotService antiBotService; - public ProtocolLibListener(FastLoginBukkit plugin, RateLimiter rateLimiter) { + public ProtocolLibListener(FastLoginBukkit plugin, AntiBotService antiBotService) { //run async in order to not block the server, because we are making api calls to Mojang super(params() .plugin(plugin) @@ -60,15 +62,15 @@ public class ProtocolLibListener extends PacketAdapter { .optionAsync()); this.plugin = plugin; - this.rateLimiter = rateLimiter; + this.antiBotService = antiBotService; } - public static void register(FastLoginBukkit plugin, RateLimiter rateLimiter) { + public static void register(FastLoginBukkit plugin, AntiBotService antiBotService) { // they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError // TODO: make synchronous processing, but do web or database requests async ProtocolLibrary.getProtocolManager() .getAsynchronousManager() - .registerAsyncHandler(new ProtocolLibListener(plugin, rateLimiter)) + .registerAsyncHandler(new ProtocolLibListener(plugin, antiBotService)) .start(); } @@ -88,12 +90,26 @@ public class ProtocolLibListener extends PacketAdapter { Player sender = packetEvent.getPlayer(); PacketType packetType = packetEvent.getPacketType(); if (packetType == START) { - if (!rateLimiter.tryAcquire()) { - plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", sender); - return; + PacketContainer packet = packetEvent.getPacket(); + + InetSocketAddress address = sender.getAddress(); + String username = packet.getGameProfiles().read(0).getName(); + + Action action = antiBotService.onIncomingConnection(address, username); + switch (action) { + case Ignore: + // just ignore + return; + case Block: + String message = plugin.getCore().getMessage("kick-antibot"); + sender.kickPlayer(message); + break; + case Continue: + default: + //player.getName() won't work at this state + onLogin(packetEvent, sender, username); + break; } - - onLogin(packetEvent, sender); } else { onEncryptionBegin(packetEvent, sender); } @@ -113,18 +129,13 @@ public class ProtocolLibListener extends PacketAdapter { plugin.getScheduler().runAsync(verifyTask); } - private void onLogin(PacketEvent packetEvent, Player player) { + private void onLogin(PacketEvent packetEvent, Player player, String username) { //this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes String sessionKey = player.getAddress().toString(); //remove old data every time on a new login in order to keep the session only for one person plugin.removeSession(player.getAddress()); - //player.getName() won't work at this state - PacketContainer packet = packetEvent.getPacket(); - - String username = packet.getGameProfiles().read(0).getName(); - if (packetEvent.getPacket().getMeta("original_name").isPresent()) { //username has been injected by ManualNameChange.java username = (String) packetEvent.getPacket().getMeta("original_name").get(); diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java index 608078c..ce84824 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java @@ -29,8 +29,9 @@ import com.github.games647.craftapi.UUIDAdapter; import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent; -import com.github.games647.fastlogin.core.RateLimiter; import com.github.games647.fastlogin.core.StoredProfile; +import com.github.games647.fastlogin.core.antibot.AntiBotService; +import com.github.games647.fastlogin.core.antibot.AntiBotService.Action; import com.github.games647.fastlogin.core.shared.JoinManagement; import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent; @@ -49,13 +50,13 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende implements Listener { private final FastLoginBukkit plugin; - private final RateLimiter rateLimiter; + private final AntiBotService antiBotService; - public ProtocolSupportListener(FastLoginBukkit plugin, RateLimiter rateLimiter) { + public ProtocolSupportListener(FastLoginBukkit plugin, AntiBotService antiBotService) { super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService()); this.plugin = plugin; - this.rateLimiter = rateLimiter; + this.antiBotService = antiBotService; } @EventHandler @@ -64,19 +65,28 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende return; } - if (!rateLimiter.tryAcquire()) { - plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", loginStartEvent.getConnection()); - return; - } - String username = loginStartEvent.getConnection().getProfile().getName(); InetSocketAddress address = loginStartEvent.getAddress(); - - //remove old data every time on a new login in order to keep the session only for one person - plugin.removeSession(address); - - ProtocolLoginSource source = new ProtocolLoginSource(loginStartEvent); - super.onLogin(username, source); + plugin.getLog().info("Incoming login request for {} from {}", username, address); + + Action action = antiBotService.onIncomingConnection(address, username); + switch (action) { + case Ignore: + // just ignore + return; + case Block: + String message = plugin.getCore().getMessage("kick-antibot"); + loginStartEvent.denyLogin(message); + break; + case Continue: + default: + //remove old data every time on a new login in order to keep the session only for one person + plugin.removeSession(address); + + ProtocolLoginSource source = new ProtocolLoginSource(loginStartEvent); + super.onLogin(username, source); + break; + } } @EventHandler diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java index a8c894d..ddd7e97 100644 --- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java @@ -32,7 +32,8 @@ import com.github.games647.fastlogin.bungee.task.AsyncPremiumCheck; import com.github.games647.fastlogin.bungee.task.FloodgateAuthTask; import com.github.games647.fastlogin.bungee.task.ForceLoginTask; import com.github.games647.fastlogin.core.StoredProfile; -import com.github.games647.fastlogin.core.antibot.RateLimiter; +import com.github.games647.fastlogin.core.antibot.AntiBotService; +import com.github.games647.fastlogin.core.antibot.AntiBotService.Action; import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService; import com.github.games647.fastlogin.core.shared.LoginSession; import com.google.common.base.Throwables; @@ -41,8 +42,10 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Field; +import java.net.InetSocketAddress; import java.util.UUID; +import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.Server; @@ -92,12 +95,12 @@ public class ConnectListener implements Listener { } private final FastLoginBungee plugin; - private final RateLimiter rateLimiter; + private final AntiBotService antiBotService; private final Property[] emptyProperties = {}; - public ConnectListener(FastLoginBungee plugin, RateLimiter rateLimiter) { + public ConnectListener(FastLoginBungee plugin, AntiBotService antiBotService) { this.plugin = plugin; - this.rateLimiter = rateLimiter; + this.antiBotService = antiBotService; } @EventHandler @@ -107,17 +110,28 @@ public class ConnectListener implements Listener { return; } - if (!rateLimiter.tryAcquire()) { - plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", connection); - return; - } - + InetSocketAddress address = preLoginEvent.getConnection().getAddress(); String username = connection.getName(); + plugin.getLog().info("Incoming login request for {} from {}", username, connection.getSocketAddress()); - preLoginEvent.registerIntent(plugin); - Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username); - plugin.getScheduler().runAsync(asyncPremiumCheck); + Action action = antiBotService.onIncomingConnection(address, username); + switch (action) { + case Ignore: + // just ignore + return; + case Block: + String message = plugin.getCore().getMessage("kick-antibot"); + preLoginEvent.setCancelReason(TextComponent.fromLegacyText(message)); + preLoginEvent.setCancelled(true); + break; + case Continue: + default: + preLoginEvent.registerIntent(plugin); + Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username); + plugin.getScheduler().runAsync(asyncPremiumCheck); + break; + } } @EventHandler(priority = EventPriority.LOWEST) @@ -140,7 +154,7 @@ public class ConnectListener implements Listener { StoredProfile playerProfile = session.getProfile(); playerProfile.setId(verifiedUUID); - // bungeecord will do this automatically so override it on disabled option + // BungeeCord will do this automatically so override it on disabled option if (uniqueIdSetter != null) { InitialHandler initialHandler = (InitialHandler) connection; diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/MojangApiConnector.java b/core/src/main/java/com/github/games647/fastlogin/core/antibot/AntiBotService.java similarity index 57% rename from core/src/main/java/com/github/games647/fastlogin/core/mojang/MojangApiConnector.java rename to core/src/main/java/com/github/games647/fastlogin/core/antibot/AntiBotService.java index 4321d6f..2848335 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/mojang/MojangApiConnector.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/antibot/AntiBotService.java @@ -23,3 +23,40 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +package com.github.games647.fastlogin.core.antibot; + +import java.net.InetSocketAddress; + +import org.slf4j.Logger; + +public class AntiBotService { + + private final Logger logger; + + private final RateLimiter rateLimiter; + private final Action limitReachedAction; + + public AntiBotService(Logger logger, RateLimiter rateLimiter, Action limitReachedAction) { + this.logger = logger; + + this.rateLimiter = rateLimiter; + this.limitReachedAction = limitReachedAction; + } + + public Action onIncomingConnection(InetSocketAddress clientAddress, String username) { + if (!rateLimiter.tryAcquire()) { + logger.warn("Anti-Bot join limit - Ignoring {}", clientAddress); + return limitReachedAction; + } + + return Action.Continue; + } + + public enum Action { + Ignore, + + Block, + + Continue; + } +} diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/ProxyAgnosticMojangResolver.java b/core/src/main/java/com/github/games647/fastlogin/core/antibot/ProxyAgnosticMojangResolver.java similarity index 100% rename from core/src/main/java/com/github/games647/fastlogin/core/mojang/ProxyAgnosticMojangResolver.java rename to core/src/main/java/com/github/games647/fastlogin/core/antibot/ProxyAgnosticMojangResolver.java diff --git a/core/src/main/java/com/github/games647/fastlogin/core/RateLimiter.java b/core/src/main/java/com/github/games647/fastlogin/core/antibot/RateLimiter.java similarity index 96% rename from core/src/main/java/com/github/games647/fastlogin/core/RateLimiter.java rename to core/src/main/java/com/github/games647/fastlogin/core/antibot/RateLimiter.java index 3ec2f75..b791ca5 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/RateLimiter.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/antibot/RateLimiter.java @@ -23,7 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.github.games647.fastlogin.core; +package com.github.games647.fastlogin.core.antibot; @FunctionalInterface public interface RateLimiter { diff --git a/core/src/main/java/com/github/games647/fastlogin/core/TickingRateLimiter.java b/core/src/main/java/com/github/games647/fastlogin/core/antibot/TickingRateLimiter.java similarity index 98% rename from core/src/main/java/com/github/games647/fastlogin/core/TickingRateLimiter.java rename to core/src/main/java/com/github/games647/fastlogin/core/antibot/TickingRateLimiter.java index 6064229..ced7da1 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/TickingRateLimiter.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/antibot/TickingRateLimiter.java @@ -23,7 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.github.games647.fastlogin.core; +package com.github.games647.fastlogin.core.antibot; import com.google.common.base.Ticker; diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/UUIDTypeAdapter.java b/core/src/main/java/com/github/games647/fastlogin/core/mojang/UUIDTypeAdapter.java deleted file mode 100644 index 4321d6f..0000000 --- a/core/src/main/java/com/github/games647/fastlogin/core/mojang/UUIDTypeAdapter.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * SPDX-License-Identifier: MIT - * - * The MIT License (MIT) - * - * Copyright (c) 2015-2022 games647 and contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java index 9e2a49a..474a8e2 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java @@ -28,8 +28,10 @@ package com.github.games647.fastlogin.core.shared; import com.github.games647.craftapi.resolver.MojangResolver; import com.github.games647.craftapi.resolver.http.RotatingProxySelector; import com.github.games647.fastlogin.core.CommonUtil; -import com.github.games647.fastlogin.core.RateLimiter; -import com.github.games647.fastlogin.core.TickingRateLimiter; +import com.github.games647.fastlogin.core.antibot.AntiBotService; +import com.github.games647.fastlogin.core.antibot.AntiBotService.Action; +import com.github.games647.fastlogin.core.antibot.RateLimiter; +import com.github.games647.fastlogin.core.antibot.TickingRateLimiter; import com.github.games647.fastlogin.core.hooks.AuthPlugin; import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator; import com.github.games647.fastlogin.core.hooks.PasswordGenerator; @@ -88,7 +90,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> { private Configuration config; private SQLStorage storage; - private RateLimiter rateLimiter; + private AntiBotService antiBot; private PasswordGenerator<P> passwordGenerator = new DefaultPasswordGenerator<>(); private AuthPlugin<P> authPlugin; @@ -122,7 +124,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> { // Initialize the resolver based on the config parameter this.resolver = this.config.getBoolean("useProxyAgnosticResolver", false) ? new ProxyAgnosticMojangResolver() : new MojangResolver(); - rateLimiter = createRateLimiter(config.getSection("anti-bot")); + antiBot = createAntiBotService(config.getSection("anti-bot")); Set<Proxy> proxies = config.getStringList("proxies") .stream() .map(HostAndPort::fromString) @@ -144,20 +146,34 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> { resolver.setOutgoingAddresses(addresses); } - private RateLimiter createRateLimiter(Configuration botSection) { - boolean enabled = botSection.getBoolean("enabled", true); - if (!enabled) { + private AntiBotService createAntiBotService(Configuration botSection) { + RateLimiter rateLimiter; + if (botSection.getBoolean("enabled", true)) { + int maxCon = botSection.getInt("connections", 200); + long expireTime = botSection.getLong("expire", 5) * 60 * 1_000L; + if (expireTime > MAX_EXPIRE_RATE) { + expireTime = MAX_EXPIRE_RATE; + } + + rateLimiter = new TickingRateLimiter(Ticker.systemTicker(), maxCon, expireTime); + } else { // no-op rate limiter - return () -> true; + rateLimiter = () -> true; } - int maxCon = botSection.getInt("connections", 200); - long expireTime = botSection.getLong("expire", 5) * 60 * 1_000L; - if (expireTime > MAX_EXPIRE_RATE) { - expireTime = MAX_EXPIRE_RATE; + Action action = Action.Ignore; + switch (botSection.getString("action", "ignore")) { + case "ignore": + action = Action.Ignore; + break; + case "block": + action = Action.Block; + break; + default: + plugin.getLog().warn("Invalid anti bot action - defaulting to ignore"); } - return new TickingRateLimiter(Ticker.systemTicker(), maxCon, expireTime); + return new AntiBotService(plugin.getLog(), rateLimiter, action); } private Configuration loadFile(String fileName) throws IOException { @@ -285,8 +301,8 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> { return authPlugin; } - public RateLimiter getRateLimiter() { - return rateLimiter; + public AntiBotService getAntiBot() { + return antiBot; } public void setAuthPluginHook(AuthPlugin<P> authPlugin) { diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index 1b0905d..3a2ab7a 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -20,6 +20,9 @@ anti-bot: connections: 600 # Amount of minutes after the first connection got inserted will expire and made available expire: 10 + # Action - Which action should be performed when the bucket is full (too many connections) + # Allowed values are: 'ignore' (FastLogin drops handling the player) or 'block' (block this incoming connection) + action: 'ignore' # Request a premium login without forcing the player to type a command # diff --git a/core/src/main/resources/messages.yml b/core/src/main/resources/messages.yml index 8b315ba..33cadaa 100644 --- a/core/src/main/resources/messages.yml +++ b/core/src/main/resources/messages.yml @@ -48,6 +48,9 @@ not-premium-other: '&4Player is not in the premium list' # Admin wanted to change the premium of a user that isn't known to the plugin player-unknown: '&4Player not in the database' +# Player kicked from anti bot feature +kick-antibot: '&4Please wait a moment!' + # ========= Bukkit/Spigot ================ # The user skipped the authentication, because it was a premium player diff --git a/core/src/test/java/com/github/games647/fastlogin/core/TickingRateLimiterTest.java b/core/src/test/java/com/github/games647/fastlogin/core/TickingRateLimiterTest.java index 2c04b6e..3f753cd 100644 --- a/core/src/test/java/com/github/games647/fastlogin/core/TickingRateLimiterTest.java +++ b/core/src/test/java/com/github/games647/fastlogin/core/TickingRateLimiterTest.java @@ -25,6 +25,8 @@ */ package com.github.games647.fastlogin.core; +import com.github.games647.fastlogin.core.antibot.TickingRateLimiter; + import java.time.Duration; import java.util.concurrent.TimeUnit; diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java index 2b51bd2..33c8c31 100644 --- a/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java +++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java @@ -27,7 +27,8 @@ package com.github.games647.fastlogin.velocity.listener; import com.github.games647.craftapi.UUIDAdapter; import com.github.games647.fastlogin.core.StoredProfile; -import com.github.games647.fastlogin.core.antibot.RateLimiter; +import com.github.games647.fastlogin.core.antibot.AntiBotService; +import com.github.games647.fastlogin.core.antibot.AntiBotService.Action; import com.github.games647.fastlogin.core.shared.LoginSession; import com.github.games647.fastlogin.velocity.FastLoginVelocity; import com.github.games647.fastlogin.velocity.VelocityLoginSession; @@ -37,6 +38,7 @@ import com.velocitypowered.api.event.Continuation; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.connection.PreLoginEvent; +import com.velocitypowered.api.event.connection.PreLoginEvent.PreLoginComponentResult; import com.velocitypowered.api.event.player.GameProfileRequestEvent; import com.velocitypowered.api.event.player.ServerConnectedEvent; import com.velocitypowered.api.proxy.InboundConnection; @@ -44,19 +46,23 @@ import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.util.GameProfile; +import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + public class ConnectListener { private final FastLoginVelocity plugin; - private final RateLimiter rateLimiter; + private final AntiBotService antiBotService; - public ConnectListener(FastLoginVelocity plugin, RateLimiter rateLimiter) { + public ConnectListener(FastLoginVelocity plugin, AntiBotService antiBotService) { this.plugin = plugin; - this.rateLimiter = rateLimiter; + this.antiBotService = antiBotService; } @Subscribe @@ -66,16 +72,28 @@ public class ConnectListener { } InboundConnection connection = preLoginEvent.getConnection(); - if (!rateLimiter.tryAcquire()) { - plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", connection); - return; - } - String username = preLoginEvent.getUsername(); - plugin.getLog().info("Incoming login request for {} from {}", username, connection.getRemoteAddress()); - - Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, connection, username, continuation, preLoginEvent); - plugin.getScheduler().runAsync(asyncPremiumCheck); + InetSocketAddress address = connection.getRemoteAddress(); + plugin.getLog().info("Incoming login request for {} from {}", username, address); + + Action action = antiBotService.onIncomingConnection(address, username); + switch (action) { + case Ignore: + // just ignore + return; + case Block: + String message = plugin.getCore().getMessage("kick-antibot"); + TextComponent messageParsed = LegacyComponentSerializer.legacyAmpersand().deserialize(message); + + PreLoginComponentResult reason = PreLoginComponentResult.denied(messageParsed); + preLoginEvent.setResult(reason); + break; + case Continue: + default: + Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, connection, username, continuation, preLoginEvent); + plugin.getScheduler().runAsync(asyncPremiumCheck); + break; + } } @Subscribe
This commit is contained in:
@ -114,9 +114,9 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
}
|
||||
|
||||
if (pluginManager.isPluginEnabled("ProtocolSupport")) {
|
||||
pluginManager.registerEvents(new ProtocolSupportListener(this, core.getRateLimiter()), this);
|
||||
pluginManager.registerEvents(new ProtocolSupportListener(this, core.getAntiBot()), this);
|
||||
} else if (pluginManager.isPluginEnabled("ProtocolLib")) {
|
||||
ProtocolLibListener.register(this, core.getRateLimiter());
|
||||
ProtocolLibListener.register(this, core.getAntiBot());
|
||||
|
||||
if (isPluginInstalled("floodgate")) {
|
||||
if (getConfig().getBoolean("floodgatePrefixWorkaround")){
|
||||
|
@ -31,8 +31,10 @@ import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.RateLimiter;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.KeyPair;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
@ -50,9 +52,9 @@ public class ProtocolLibListener extends PacketAdapter {
|
||||
//just create a new once on plugin enable. This used for verify token generation
|
||||
private final SecureRandom random = new SecureRandom();
|
||||
private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
|
||||
private final RateLimiter rateLimiter;
|
||||
private final AntiBotService antiBotService;
|
||||
|
||||
public ProtocolLibListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
|
||||
public ProtocolLibListener(FastLoginBukkit plugin, AntiBotService antiBotService) {
|
||||
//run async in order to not block the server, because we are making api calls to Mojang
|
||||
super(params()
|
||||
.plugin(plugin)
|
||||
@ -60,15 +62,15 @@ public class ProtocolLibListener extends PacketAdapter {
|
||||
.optionAsync());
|
||||
|
||||
this.plugin = plugin;
|
||||
this.rateLimiter = rateLimiter;
|
||||
this.antiBotService = antiBotService;
|
||||
}
|
||||
|
||||
public static void register(FastLoginBukkit plugin, RateLimiter rateLimiter) {
|
||||
public static void register(FastLoginBukkit plugin, AntiBotService antiBotService) {
|
||||
// they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError
|
||||
// TODO: make synchronous processing, but do web or database requests async
|
||||
ProtocolLibrary.getProtocolManager()
|
||||
.getAsynchronousManager()
|
||||
.registerAsyncHandler(new ProtocolLibListener(plugin, rateLimiter))
|
||||
.registerAsyncHandler(new ProtocolLibListener(plugin, antiBotService))
|
||||
.start();
|
||||
}
|
||||
|
||||
@ -88,12 +90,26 @@ public class ProtocolLibListener extends PacketAdapter {
|
||||
Player sender = packetEvent.getPlayer();
|
||||
PacketType packetType = packetEvent.getPacketType();
|
||||
if (packetType == START) {
|
||||
if (!rateLimiter.tryAcquire()) {
|
||||
plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", sender);
|
||||
return;
|
||||
}
|
||||
PacketContainer packet = packetEvent.getPacket();
|
||||
|
||||
onLogin(packetEvent, sender);
|
||||
InetSocketAddress address = sender.getAddress();
|
||||
String username = packet.getGameProfiles().read(0).getName();
|
||||
|
||||
Action action = antiBotService.onIncomingConnection(address, username);
|
||||
switch (action) {
|
||||
case Ignore:
|
||||
// just ignore
|
||||
return;
|
||||
case Block:
|
||||
String message = plugin.getCore().getMessage("kick-antibot");
|
||||
sender.kickPlayer(message);
|
||||
break;
|
||||
case Continue:
|
||||
default:
|
||||
//player.getName() won't work at this state
|
||||
onLogin(packetEvent, sender, username);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
onEncryptionBegin(packetEvent, sender);
|
||||
}
|
||||
@ -113,18 +129,13 @@ public class ProtocolLibListener extends PacketAdapter {
|
||||
plugin.getScheduler().runAsync(verifyTask);
|
||||
}
|
||||
|
||||
private void onLogin(PacketEvent packetEvent, Player player) {
|
||||
private void onLogin(PacketEvent packetEvent, Player player, String username) {
|
||||
//this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
|
||||
String sessionKey = player.getAddress().toString();
|
||||
|
||||
//remove old data every time on a new login in order to keep the session only for one person
|
||||
plugin.removeSession(player.getAddress());
|
||||
|
||||
//player.getName() won't work at this state
|
||||
PacketContainer packet = packetEvent.getPacket();
|
||||
|
||||
String username = packet.getGameProfiles().read(0).getName();
|
||||
|
||||
if (packetEvent.getPacket().getMeta("original_name").isPresent()) {
|
||||
//username has been injected by ManualNameChange.java
|
||||
username = (String) packetEvent.getPacket().getMeta("original_name").get();
|
||||
|
@ -29,8 +29,9 @@ import com.github.games647.craftapi.UUIDAdapter;
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent;
|
||||
import com.github.games647.fastlogin.core.RateLimiter;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
|
||||
import com.github.games647.fastlogin.core.shared.JoinManagement;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
||||
|
||||
@ -49,13 +50,13 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
|
||||
implements Listener {
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
private final RateLimiter rateLimiter;
|
||||
private final AntiBotService antiBotService;
|
||||
|
||||
public ProtocolSupportListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
|
||||
public ProtocolSupportListener(FastLoginBukkit plugin, AntiBotService antiBotService) {
|
||||
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
|
||||
|
||||
this.plugin = plugin;
|
||||
this.rateLimiter = rateLimiter;
|
||||
this.antiBotService = antiBotService;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@ -64,19 +65,28 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rateLimiter.tryAcquire()) {
|
||||
plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", loginStartEvent.getConnection());
|
||||
return;
|
||||
}
|
||||
|
||||
String username = loginStartEvent.getConnection().getProfile().getName();
|
||||
InetSocketAddress address = loginStartEvent.getAddress();
|
||||
plugin.getLog().info("Incoming login request for {} from {}", username, address);
|
||||
|
||||
//remove old data every time on a new login in order to keep the session only for one person
|
||||
plugin.removeSession(address);
|
||||
Action action = antiBotService.onIncomingConnection(address, username);
|
||||
switch (action) {
|
||||
case Ignore:
|
||||
// just ignore
|
||||
return;
|
||||
case Block:
|
||||
String message = plugin.getCore().getMessage("kick-antibot");
|
||||
loginStartEvent.denyLogin(message);
|
||||
break;
|
||||
case Continue:
|
||||
default:
|
||||
//remove old data every time on a new login in order to keep the session only for one person
|
||||
plugin.removeSession(address);
|
||||
|
||||
ProtocolLoginSource source = new ProtocolLoginSource(loginStartEvent);
|
||||
super.onLogin(username, source);
|
||||
ProtocolLoginSource source = new ProtocolLoginSource(loginStartEvent);
|
||||
super.onLogin(username, source);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
|
@ -32,7 +32,8 @@ import com.github.games647.fastlogin.bungee.task.AsyncPremiumCheck;
|
||||
import com.github.games647.fastlogin.bungee.task.FloodgateAuthTask;
|
||||
import com.github.games647.fastlogin.bungee.task.ForceLoginTask;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.antibot.RateLimiter;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
import com.google.common.base.Throwables;
|
||||
@ -41,8 +42,10 @@ import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
@ -92,12 +95,12 @@ public class ConnectListener implements Listener {
|
||||
}
|
||||
|
||||
private final FastLoginBungee plugin;
|
||||
private final RateLimiter rateLimiter;
|
||||
private final AntiBotService antiBotService;
|
||||
private final Property[] emptyProperties = {};
|
||||
|
||||
public ConnectListener(FastLoginBungee plugin, RateLimiter rateLimiter) {
|
||||
public ConnectListener(FastLoginBungee plugin, AntiBotService antiBotService) {
|
||||
this.plugin = plugin;
|
||||
this.rateLimiter = rateLimiter;
|
||||
this.antiBotService = antiBotService;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@ -107,17 +110,28 @@ public class ConnectListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rateLimiter.tryAcquire()) {
|
||||
plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", connection);
|
||||
return;
|
||||
}
|
||||
|
||||
InetSocketAddress address = preLoginEvent.getConnection().getAddress();
|
||||
String username = connection.getName();
|
||||
|
||||
plugin.getLog().info("Incoming login request for {} from {}", username, connection.getSocketAddress());
|
||||
|
||||
preLoginEvent.registerIntent(plugin);
|
||||
Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username);
|
||||
plugin.getScheduler().runAsync(asyncPremiumCheck);
|
||||
Action action = antiBotService.onIncomingConnection(address, username);
|
||||
switch (action) {
|
||||
case Ignore:
|
||||
// just ignore
|
||||
return;
|
||||
case Block:
|
||||
String message = plugin.getCore().getMessage("kick-antibot");
|
||||
preLoginEvent.setCancelReason(TextComponent.fromLegacyText(message));
|
||||
preLoginEvent.setCancelled(true);
|
||||
break;
|
||||
case Continue:
|
||||
default:
|
||||
preLoginEvent.registerIntent(plugin);
|
||||
Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username);
|
||||
plugin.getScheduler().runAsync(asyncPremiumCheck);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
@ -140,7 +154,7 @@ public class ConnectListener implements Listener {
|
||||
StoredProfile playerProfile = session.getProfile();
|
||||
playerProfile.setId(verifiedUUID);
|
||||
|
||||
// bungeecord will do this automatically so override it on disabled option
|
||||
// BungeeCord will do this automatically so override it on disabled option
|
||||
if (uniqueIdSetter != null) {
|
||||
InitialHandler initialHandler = (InitialHandler) connection;
|
||||
|
||||
|
@ -23,3 +23,40 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.antibot;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class AntiBotService {
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
private final RateLimiter rateLimiter;
|
||||
private final Action limitReachedAction;
|
||||
|
||||
public AntiBotService(Logger logger, RateLimiter rateLimiter, Action limitReachedAction) {
|
||||
this.logger = logger;
|
||||
|
||||
this.rateLimiter = rateLimiter;
|
||||
this.limitReachedAction = limitReachedAction;
|
||||
}
|
||||
|
||||
public Action onIncomingConnection(InetSocketAddress clientAddress, String username) {
|
||||
if (!rateLimiter.tryAcquire()) {
|
||||
logger.warn("Anti-Bot join limit - Ignoring {}", clientAddress);
|
||||
return limitReachedAction;
|
||||
}
|
||||
|
||||
return Action.Continue;
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
Ignore,
|
||||
|
||||
Block,
|
||||
|
||||
Continue;
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core;
|
||||
package com.github.games647.fastlogin.core.antibot;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface RateLimiter {
|
@ -23,7 +23,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core;
|
||||
package com.github.games647.fastlogin.core.antibot;
|
||||
|
||||
import com.google.common.base.Ticker;
|
||||
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2022 games647 and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
@ -28,8 +28,10 @@ package com.github.games647.fastlogin.core.shared;
|
||||
import com.github.games647.craftapi.resolver.MojangResolver;
|
||||
import com.github.games647.craftapi.resolver.http.RotatingProxySelector;
|
||||
import com.github.games647.fastlogin.core.CommonUtil;
|
||||
import com.github.games647.fastlogin.core.RateLimiter;
|
||||
import com.github.games647.fastlogin.core.TickingRateLimiter;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
|
||||
import com.github.games647.fastlogin.core.antibot.RateLimiter;
|
||||
import com.github.games647.fastlogin.core.antibot.TickingRateLimiter;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator;
|
||||
import com.github.games647.fastlogin.core.hooks.PasswordGenerator;
|
||||
@ -88,7 +90,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
|
||||
private Configuration config;
|
||||
private SQLStorage storage;
|
||||
private RateLimiter rateLimiter;
|
||||
private AntiBotService antiBot;
|
||||
private PasswordGenerator<P> passwordGenerator = new DefaultPasswordGenerator<>();
|
||||
private AuthPlugin<P> authPlugin;
|
||||
|
||||
@ -122,7 +124,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
// Initialize the resolver based on the config parameter
|
||||
this.resolver = this.config.getBoolean("useProxyAgnosticResolver", false) ? new ProxyAgnosticMojangResolver() : new MojangResolver();
|
||||
|
||||
rateLimiter = createRateLimiter(config.getSection("anti-bot"));
|
||||
antiBot = createAntiBotService(config.getSection("anti-bot"));
|
||||
Set<Proxy> proxies = config.getStringList("proxies")
|
||||
.stream()
|
||||
.map(HostAndPort::fromString)
|
||||
@ -144,20 +146,34 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
resolver.setOutgoingAddresses(addresses);
|
||||
}
|
||||
|
||||
private RateLimiter createRateLimiter(Configuration botSection) {
|
||||
boolean enabled = botSection.getBoolean("enabled", true);
|
||||
if (!enabled) {
|
||||
private AntiBotService createAntiBotService(Configuration botSection) {
|
||||
RateLimiter rateLimiter;
|
||||
if (botSection.getBoolean("enabled", true)) {
|
||||
int maxCon = botSection.getInt("connections", 200);
|
||||
long expireTime = botSection.getLong("expire", 5) * 60 * 1_000L;
|
||||
if (expireTime > MAX_EXPIRE_RATE) {
|
||||
expireTime = MAX_EXPIRE_RATE;
|
||||
}
|
||||
|
||||
rateLimiter = new TickingRateLimiter(Ticker.systemTicker(), maxCon, expireTime);
|
||||
} else {
|
||||
// no-op rate limiter
|
||||
return () -> true;
|
||||
rateLimiter = () -> true;
|
||||
}
|
||||
|
||||
int maxCon = botSection.getInt("connections", 200);
|
||||
long expireTime = botSection.getLong("expire", 5) * 60 * 1_000L;
|
||||
if (expireTime > MAX_EXPIRE_RATE) {
|
||||
expireTime = MAX_EXPIRE_RATE;
|
||||
Action action = Action.Ignore;
|
||||
switch (botSection.getString("action", "ignore")) {
|
||||
case "ignore":
|
||||
action = Action.Ignore;
|
||||
break;
|
||||
case "block":
|
||||
action = Action.Block;
|
||||
break;
|
||||
default:
|
||||
plugin.getLog().warn("Invalid anti bot action - defaulting to ignore");
|
||||
}
|
||||
|
||||
return new TickingRateLimiter(Ticker.systemTicker(), maxCon, expireTime);
|
||||
return new AntiBotService(plugin.getLog(), rateLimiter, action);
|
||||
}
|
||||
|
||||
private Configuration loadFile(String fileName) throws IOException {
|
||||
@ -285,8 +301,8 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
return authPlugin;
|
||||
}
|
||||
|
||||
public RateLimiter getRateLimiter() {
|
||||
return rateLimiter;
|
||||
public AntiBotService getAntiBot() {
|
||||
return antiBot;
|
||||
}
|
||||
|
||||
public void setAuthPluginHook(AuthPlugin<P> authPlugin) {
|
||||
|
@ -20,6 +20,9 @@ anti-bot:
|
||||
connections: 600
|
||||
# Amount of minutes after the first connection got inserted will expire and made available
|
||||
expire: 10
|
||||
# Action - Which action should be performed when the bucket is full (too many connections)
|
||||
# Allowed values are: 'ignore' (FastLogin drops handling the player) or 'block' (block this incoming connection)
|
||||
action: 'ignore'
|
||||
|
||||
# Request a premium login without forcing the player to type a command
|
||||
#
|
||||
|
@ -48,6 +48,9 @@ not-premium-other: '&4Player is not in the premium list'
|
||||
# Admin wanted to change the premium of a user that isn't known to the plugin
|
||||
player-unknown: '&4Player not in the database'
|
||||
|
||||
# Player kicked from anti bot feature
|
||||
kick-antibot: '&4Please wait a moment!'
|
||||
|
||||
# ========= Bukkit/Spigot ================
|
||||
|
||||
# The user skipped the authentication, because it was a premium player
|
||||
|
@ -25,6 +25,8 @@
|
||||
*/
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import com.github.games647.fastlogin.core.antibot.TickingRateLimiter;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -27,7 +27,8 @@ package com.github.games647.fastlogin.velocity.listener;
|
||||
|
||||
import com.github.games647.craftapi.UUIDAdapter;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.antibot.RateLimiter;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
import com.github.games647.fastlogin.velocity.FastLoginVelocity;
|
||||
import com.github.games647.fastlogin.velocity.VelocityLoginSession;
|
||||
@ -37,6 +38,7 @@ import com.velocitypowered.api.event.Continuation;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.DisconnectEvent;
|
||||
import com.velocitypowered.api.event.connection.PreLoginEvent;
|
||||
import com.velocitypowered.api.event.connection.PreLoginEvent.PreLoginComponentResult;
|
||||
import com.velocitypowered.api.event.player.GameProfileRequestEvent;
|
||||
import com.velocitypowered.api.event.player.ServerConnectedEvent;
|
||||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
@ -44,19 +46,23 @@ import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.util.GameProfile;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
public class ConnectListener {
|
||||
|
||||
private final FastLoginVelocity plugin;
|
||||
private final RateLimiter rateLimiter;
|
||||
private final AntiBotService antiBotService;
|
||||
|
||||
public ConnectListener(FastLoginVelocity plugin, RateLimiter rateLimiter) {
|
||||
public ConnectListener(FastLoginVelocity plugin, AntiBotService antiBotService) {
|
||||
this.plugin = plugin;
|
||||
this.rateLimiter = rateLimiter;
|
||||
this.antiBotService = antiBotService;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@ -66,16 +72,28 @@ public class ConnectListener {
|
||||
}
|
||||
|
||||
InboundConnection connection = preLoginEvent.getConnection();
|
||||
if (!rateLimiter.tryAcquire()) {
|
||||
plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", connection);
|
||||
return;
|
||||
}
|
||||
|
||||
String username = preLoginEvent.getUsername();
|
||||
plugin.getLog().info("Incoming login request for {} from {}", username, connection.getRemoteAddress());
|
||||
InetSocketAddress address = connection.getRemoteAddress();
|
||||
plugin.getLog().info("Incoming login request for {} from {}", username, address);
|
||||
|
||||
Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, connection, username, continuation, preLoginEvent);
|
||||
plugin.getScheduler().runAsync(asyncPremiumCheck);
|
||||
Action action = antiBotService.onIncomingConnection(address, username);
|
||||
switch (action) {
|
||||
case Ignore:
|
||||
// just ignore
|
||||
return;
|
||||
case Block:
|
||||
String message = plugin.getCore().getMessage("kick-antibot");
|
||||
TextComponent messageParsed = LegacyComponentSerializer.legacyAmpersand().deserialize(message);
|
||||
|
||||
PreLoginComponentResult reason = PreLoginComponentResult.denied(messageParsed);
|
||||
preLoginEvent.setResult(reason);
|
||||
break;
|
||||
case Continue:
|
||||
default:
|
||||
Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, connection, username, continuation, preLoginEvent);
|
||||
plugin.getScheduler().runAsync(asyncPremiumCheck);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
|
Reference in New Issue
Block a user