Merge pull request #823 from games647/805-more-antibot-features

Add support for blocking incoming connection on AntiBot detection
This commit is contained in:
games647
2022-06-18 18:15:52 +02:00
committed by GitHub
17 changed files with 228 additions and 132 deletions

View File

@ -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")){

View File

@ -30,9 +30,12 @@ import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
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 +53,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 +63,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 +91,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);
}
@ -108,23 +125,24 @@ public class ProtocolLibListener extends PacketAdapter {
private void onEncryptionBegin(PacketEvent packetEvent, Player sender) {
byte[] sharedSecret = packetEvent.getPacket().getByteArrays().read(0);
packetEvent.getAsyncMarker().incrementProcessingDelay();
Runnable verifyTask = new VerifyResponseTask(plugin, packetEvent, sender, sharedSecret, keyPair);
plugin.getScheduler().runAsync(verifyTask);
BukkitLoginSession session = plugin.getSession(sender.getAddress());
if (session == null) {
plugin.getLog().warn("GameProfile {} tried to send encryption response at invalid state", sender.getAddress());
sender.kickPlayer(plugin.getCore().getMessage("invalid-request"));
} else {
packetEvent.getAsyncMarker().incrementProcessingDelay();
Runnable verifyTask = new VerifyResponseTask(plugin, packetEvent, sender, session, sharedSecret, keyPair);
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();

View File

@ -79,16 +79,20 @@ public class VerifyResponseTask implements Runnable {
private final Player player;
private final BukkitLoginSession session;
private final byte[] sharedSecret;
private static Method encryptMethod;
private static Method cipherMethod;
public VerifyResponseTask(FastLoginBukkit plugin, PacketEvent packetEvent, Player player,
public VerifyResponseTask(FastLoginBukkit plugin, PacketEvent packetEvent,
Player player, BukkitLoginSession session,
byte[] sharedSecret, KeyPair keyPair) {
this.plugin = plugin;
this.packetEvent = packetEvent;
this.player = player;
this.session = session;
this.sharedSecret = Arrays.copyOf(sharedSecret, sharedSecret.length);
this.serverKey = keyPair;
}
@ -96,14 +100,7 @@ public class VerifyResponseTask implements Runnable {
@Override
public void run() {
try {
BukkitLoginSession session = plugin.getSession(player.getAddress());
if (session == null) {
disconnect("invalid-request",
"GameProfile {0} tried to send encryption response at invalid state",
player.getAddress());
} else {
verifyResponse(session);
}
verifyResponse(session);
} finally {
//this is a fake packet; it shouldn't be sent to the server
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
@ -143,25 +140,7 @@ public class VerifyResponseTask implements Runnable {
InetAddress address = socketAddress.getAddress();
Optional<Verification> response = resolver.hasJoined(requestedUsername, serverId, address);
if (response.isPresent()) {
Verification verification = response.get();
plugin.getLog().info("Profile {} has a verified premium account", requestedUsername);
String realUsername = verification.getName();
if (realUsername == null) {
disconnect("invalid-session", "Username field null for {}", requestedUsername);
return;
}
SkinProperty[] properties = verification.getProperties();
if (properties.length > 0) {
session.setSkinProperty(properties[0]);
}
session.setVerifiedUsername(realUsername);
session.setUuid(verification.getId());
session.setVerified(true);
setPremiumUUID(session.getUuid());
receiveFakeStartPacket(realUsername);
encryptConnection(session, requestedUsername, response.get());
} else {
//user tried to fake an authentication
disconnect("invalid-session", "GameProfile {} ({}) tried to log in with an invalid session. ServerId: {}", session.getRequestUsername(), socketAddress, serverId);
@ -171,6 +150,27 @@ public class VerifyResponseTask implements Runnable {
}
}
private void encryptConnection(BukkitLoginSession session, String requestedUsername, Verification verification) {
plugin.getLog().info("Profile {} has a verified premium account", requestedUsername);
String realUsername = verification.getName();
if (realUsername == null) {
disconnect("invalid-session", "Username field null for {}", requestedUsername);
return;
}
SkinProperty[] properties = verification.getProperties();
if (properties.length > 0) {
session.setSkinProperty(properties[0]);
}
session.setVerifiedUsername(realUsername);
session.setUuid(verification.getId());
session.setVerified(true);
setPremiumUUID(session.getUuid());
receiveFakeStartPacket(realUsername);
}
private void setPremiumUUID(UUID premiumUUID) {
if (plugin.getConfig().getBoolean("premiumUuid") && premiumUUID != null) {
try {

View File

@ -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

View File

@ -100,7 +100,7 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
//events
PluginManager pluginManager = getProxy().getPluginManager();
ConnectListener connectListener = new ConnectListener(this, core.getRateLimiter());
ConnectListener connectListener = new ConnectListener(this, core.getAntiBot());
pluginManager.registerListener(this, connectListener);
pluginManager.registerListener(this, new PluginMessageListener(this));

View File

@ -31,8 +31,9 @@ import com.github.games647.fastlogin.bungee.FastLoginBungee;
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.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.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;

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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;

View File

@ -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.
*/

View File

@ -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("anti-bot.connections", 200);
long expireTime = botSection.getLong("anti-bot.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) {

View File

@ -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
#

View File

@ -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

View File

@ -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;

View File

@ -96,7 +96,7 @@ public class FastLoginVelocity implements PlatformPlugin<CommandSource> {
return;
}
server.getEventManager().register(this, new ConnectListener(this, core.getRateLimiter()));
server.getEventManager().register(this, new ConnectListener(this, core.getAntiBot()));
server.getEventManager().register(this, new PluginMessageListener(this));
server.getChannelRegistrar().register(MinecraftChannelIdentifier.create(getName(), ChangePremiumMessage.CHANGE_CHANNEL));
server.getChannelRegistrar().register(MinecraftChannelIdentifier.create(getName(), SuccessMessage.SUCCESS_CHANNEL));

View File

@ -26,8 +26,9 @@
package com.github.games647.fastlogin.velocity.listener;
import com.github.games647.craftapi.UUIDAdapter;
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.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