From 08d5bd1686d021b4ea1d27aa5fef85ca0a2743ab Mon Sep 17 00:00:00 2001
From: LinsaFTW <25271111+linsaftw@users.noreply.github.com>
Date: Fri, 4 Mar 2022 13:35:53 -0300
Subject: [PATCH] Antibot System
diff --git a/flamecord/pom.xml b/flamecord/pom.xml
index f4bf5ec6..4a0d8e5d 100644
--- a/flamecord/pom.xml
+++ b/flamecord/pom.xml
@@ -30,6 +30,11 @@
${project.version}
compile
+
+ com.maxmind.db
+ maxmind-db
+ 2.0.0
+
diff --git a/flamecord/src/main/java/dev/_2lstudios/antibot/AccountsCheck.java b/flamecord/src/main/java/dev/_2lstudios/antibot/AccountsCheck.java
new file mode 100644
index 00000000..dfee9bf8
--- /dev/null
+++ b/flamecord/src/main/java/dev/_2lstudios/antibot/AccountsCheck.java
@@ -0,0 +1,36 @@
+package dev._2lstudios.antibot;
+
+import java.net.SocketAddress;
+import java.util.Collection;
+
+import dev._2lstudios.flamecord.FlameCord;
+import dev._2lstudios.flamecord.configuration.FlameCordConfiguration;
+
+public class AccountsCheck {
+ private final AddressDataManager addressDataManager;
+
+ public AccountsCheck(final AddressDataManager addressDataManager) {
+ this.addressDataManager = addressDataManager;
+ }
+
+ public boolean check(final SocketAddress socketAddress, final String nickname) {
+ final FlameCordConfiguration config = FlameCord.getInstance().getFlameCordConfiguration();
+
+ if (config.isAntibotAccountsEnabled()) {
+ final AddressData addressData = addressDataManager.getAddressData(socketAddress);
+ final Collection nicknames = addressData.getNicknames();
+
+ if (nicknames.size() > config.getAntibotAccountsLimit()) {
+ nicknames.remove(nickname);
+
+ if (config.isAntibotAccountsFirewall()) {
+ addressData.firewall();
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/flamecord/src/main/java/dev/_2lstudios/antibot/AddressData.java b/flamecord/src/main/java/dev/_2lstudios/antibot/AddressData.java
new file mode 100644
index 00000000..cd8220d1
--- /dev/null
+++ b/flamecord/src/main/java/dev/_2lstudios/antibot/AddressData.java
@@ -0,0 +1,149 @@
+package dev._2lstudios.antibot;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+
+import dev._2lstudios.flamecord.FlameCord;
+
+public class AddressData {
+ private final Collection nicknames = new HashSet<>();
+ private final String hostString;
+ private String lastNickname = "";
+ private String country = null;
+ private long lastPing = 0;
+ private long penultimateConnection = 0;
+ private long lastConnection = 0;
+ private long lastFirewall = 0;
+ private int pingsSecond = 0;
+ private int totalPings = 0;
+ private int connectionsSecond = 0;
+ private int totalConnections = 0;
+
+ public AddressData(final String hostString) {
+ this.hostString = hostString;
+ }
+
+ public Collection getNicknames() {
+ return nicknames;
+ }
+
+ public String getLastNickname() {
+ return lastNickname;
+ }
+
+ public void addNickname(final String nickname) {
+ if (!lastNickname.equals(nickname)) {
+ this.lastNickname = nickname;
+ this.totalConnections = 1;
+ }
+
+ if (!this.nicknames.contains(nickname)) {
+ this.nicknames.add(nickname);
+ }
+ }
+
+ public long getPenultimateConnection() {
+ return penultimateConnection;
+ }
+
+ public long getTimeSincePenultimateConnection() {
+ return System.currentTimeMillis() - penultimateConnection;
+ }
+
+ public long getLastConnection() {
+ return lastConnection;
+ }
+
+ public long getTimeSinceLastConnection() {
+ return System.currentTimeMillis() - lastConnection;
+ }
+
+ private void updatePingsSecond() {
+ if (System.currentTimeMillis() - lastPing >= 1000) {
+ pingsSecond = 0;
+ }
+ }
+
+ public int getPingsSecond() {
+ updatePingsSecond();
+ return pingsSecond;
+ }
+
+ public void addPing() {
+ updatePingsSecond();
+ lastPing = System.currentTimeMillis();
+ pingsSecond++;
+ totalPings++;
+ }
+
+ public int getTotalPings() {
+ return totalPings;
+ }
+
+ private void updateConnectionsSecond() {
+ if (System.currentTimeMillis() - lastConnection >= 1000) {
+ connectionsSecond = 0;
+ }
+ }
+
+ public int getConnectionsSecond() {
+ updateConnectionsSecond();
+ return connectionsSecond;
+ }
+
+ public void addConnection() {
+ final long currentTime = System.currentTimeMillis();
+
+ updateConnectionsSecond();
+ penultimateConnection = lastConnection == 0 ? currentTime : lastConnection;
+ lastConnection = currentTime;
+ connectionsSecond++;
+ totalConnections++;
+ }
+
+ public int getTotalConnections() {
+ return totalConnections;
+ }
+
+ public boolean isFirewalled() {
+ return System.currentTimeMillis() - lastFirewall < FlameCord.getInstance().getFlameCordConfiguration()
+ .getAntibotFirewallExpire() * 1000;
+ }
+
+ public String getHostString() {
+ return hostString;
+ }
+
+ public void firewall() {
+ if (!hostString.equals("127.0.0.1")) {
+ if (FlameCord.getInstance().getFlameCordConfiguration().isAntibotFirewallIpset()) {
+ Runtime runtime = Runtime.getRuntime();
+
+ try {
+ runtime.exec("ipset add flamecord_blacklist " + hostString);
+ } catch (IOException exception) {
+ // Ignored
+ }
+ }
+
+ this.lastFirewall = System.currentTimeMillis();
+ }
+ }
+
+ public void setTotalConnections(final int totalConnections) {
+ this.totalConnections = totalConnections;
+ }
+
+ public String setCountry(final String country) {
+ return this.country = country;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public boolean hasNickname(final String nickname) {
+ return nicknames.contains(nickname);
+ }
+}
diff --git a/flamecord/src/main/java/dev/_2lstudios/antibot/AddressDataManager.java b/flamecord/src/main/java/dev/_2lstudios/antibot/AddressDataManager.java
new file mode 100644
index 00000000..3f6e4186
--- /dev/null
+++ b/flamecord/src/main/java/dev/_2lstudios/antibot/AddressDataManager.java
@@ -0,0 +1,25 @@
+package dev._2lstudios.antibot;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.HashMap;
+import java.util.Map;
+
+public class AddressDataManager {
+ private final Map addressData = new HashMap<>();
+
+ public AddressData getAddressData(final SocketAddress address) {
+ final InetSocketAddress iNetSocketAddress = (InetSocketAddress) address;
+ final String addressString = iNetSocketAddress.getHostString();
+
+ if (addressData.containsKey(addressString)) {
+ return addressData.get(addressString);
+ } else {
+ AddressData data = new AddressData(addressString);
+
+ addressData.put(addressString, data);
+
+ return data;
+ }
+ }
+}
diff --git a/flamecord/src/main/java/dev/_2lstudios/antibot/CheckManager.java b/flamecord/src/main/java/dev/_2lstudios/antibot/CheckManager.java
new file mode 100644
index 00000000..137ed980
--- /dev/null
+++ b/flamecord/src/main/java/dev/_2lstudios/antibot/CheckManager.java
@@ -0,0 +1,41 @@
+package dev._2lstudios.antibot;
+
+import dev._2lstudios.flamecord.configuration.FlameCordConfiguration;
+import lombok.Getter;
+
+public class CheckManager {
+ @Getter
+ private final AccountsCheck accountsCheck;
+ @Getter
+ private final CountryCheck countryCheck;
+ @Getter
+ private final FastChatCheck fastChatCheck;
+ @Getter
+ private final FirewallCheck firewallCheck;
+ @Getter
+ private final NicknameCheck nicknameCheck;
+ @Getter
+ private final PasswordCheck passwordCheck;
+ @Getter
+ private final RatelimitCheck ratelimitCheck;
+ @Getter
+ private final ReconnectCheck reconnectCheck;
+
+ public CheckManager(final AddressDataManager addressDataManager, final FlameCordConfiguration flameCordConfiguration) {
+ this.accountsCheck = new AccountsCheck(addressDataManager);
+ this.countryCheck = new CountryCheck(addressDataManager);
+ this.fastChatCheck = new FastChatCheck(addressDataManager);
+ this.firewallCheck = new FirewallCheck(addressDataManager, flameCordConfiguration);
+ this.nicknameCheck = new NicknameCheck(addressDataManager);
+ this.passwordCheck = new PasswordCheck(addressDataManager);
+ this.ratelimitCheck = new RatelimitCheck(addressDataManager);
+ this.reconnectCheck = new ReconnectCheck(addressDataManager);
+
+ this.countryCheck.load();
+ this.firewallCheck.load();
+ }
+
+ public void unload() {
+ this.countryCheck.unload();
+ }
+}
diff --git a/flamecord/src/main/java/dev/_2lstudios/antibot/CountryCheck.java b/flamecord/src/main/java/dev/_2lstudios/antibot/CountryCheck.java
new file mode 100644
index 00000000..8507e1a7
--- /dev/null
+++ b/flamecord/src/main/java/dev/_2lstudios/antibot/CountryCheck.java
@@ -0,0 +1,140 @@
+package dev._2lstudios.antibot;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.URL;
+import java.nio.file.Files;
+
+import com.maxmind.db.CHMCache;
+import com.maxmind.db.MaxMindDbConstructor;
+import com.maxmind.db.MaxMindDbParameter;
+import com.maxmind.db.Reader;
+
+import dev._2lstudios.flamecord.FlameCord;
+import dev._2lstudios.flamecord.configuration.FlameCordConfiguration;
+
+public class CountryCheck {
+ private final AddressDataManager addressDataManager;
+ private Reader maxMindReader;
+
+ public CountryCheck(final AddressDataManager addressDataManager) {
+ this.addressDataManager = addressDataManager;
+ }
+
+ public void download(final URL url, final File file) throws Exception {
+ try (InputStream in = url.openStream()) {
+ Files.copy(in, file.toPath());
+ }
+ }
+
+ public void load() {
+ final File file = new File("GeoLite2-Country.mmdb");
+
+ try {
+ if (!file.exists()) {
+ System.out.println("Starting download of MaxMindDB (This will take some seconds...)");
+ download(new URL("https://git.io/GeoLite2-Country.mmdb"), file);
+ }
+
+ this.maxMindReader = new Reader(file, new CHMCache());
+ } catch (final Exception exception) {
+ // Ignored
+ }
+ }
+
+ public void unload() {
+ try {
+ if (this.maxMindReader != null) {
+ this.maxMindReader.close();
+ }
+ } catch (final IOException ex) {
+ // Ignored
+ }
+ }
+
+ private boolean isBlacklisted(final FlameCordConfiguration config, final String isoCode) {
+ for (final String blacklisted : config.getAntibotCountryBlacklist()) {
+ if (isoCode.contains(blacklisted)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static class LookupResult {
+ private final Country country;
+
+ @MaxMindDbConstructor
+ public LookupResult(@MaxMindDbParameter(name = "country") final Country country) {
+ this.country = country;
+ }
+
+ public Country getCountry() {
+ return this.country;
+ }
+ }
+
+ public static class Country {
+ private final String isoCode;
+
+ @MaxMindDbConstructor
+ public Country(@MaxMindDbParameter(name = "iso_code") final String isoCode) {
+ this.isoCode = isoCode;
+ }
+
+ public String getIsoCode() {
+ return this.isoCode;
+ }
+ }
+
+ public String getIsoCode(final InetAddress address) {
+ try {
+ final LookupResult lookupResult = maxMindReader.get(address, LookupResult.class);
+
+ if (lookupResult == null) {
+ return "LOCAL";
+ } else {
+ final Country country = lookupResult.getCountry();
+ final String isoCode = country.getIsoCode();
+
+ return isoCode;
+ }
+ } catch (final IOException exception) {
+ // Ignored
+ }
+
+ return null;
+ }
+
+ public boolean check(final SocketAddress socketAddress) {
+ final FlameCordConfiguration config = FlameCord.getInstance().getFlameCordConfiguration();
+
+ if (config.isAntibotCountryEnabled()) {
+ final AddressData addressData = addressDataManager.getAddressData(socketAddress);
+ final String addressCountry = addressData.getCountry();
+ final String country;
+
+ if (addressCountry != null) {
+ country = addressCountry;
+ } else {
+ country = getIsoCode(((InetSocketAddress) socketAddress).getAddress());
+ addressData.setCountry(country);
+ }
+
+ if (country != null && isBlacklisted(config, country)) {
+ if (config.isAntibotCountryFirewall()) {
+ addressData.firewall();
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/flamecord/src/main/java/dev/_2lstudios/antibot/FastChatCheck.java b/flamecord/src/main/java/dev/_2lstudios/antibot/FastChatCheck.java
new file mode 100644
index 00000000..76902c27
--- /dev/null
+++ b/flamecord/src/main/java/dev/_2lstudios/antibot/FastChatCheck.java
@@ -0,0 +1,32 @@
+package dev._2lstudios.antibot;
+
+import java.net.SocketAddress;
+
+import dev._2lstudios.flamecord.FlameCord;
+import dev._2lstudios.flamecord.configuration.FlameCordConfiguration;
+
+public class FastChatCheck {
+ private final AddressDataManager addressDataManager;
+
+ public FastChatCheck(final AddressDataManager addressDataManager) {
+ this.addressDataManager = addressDataManager;
+ }
+
+ public boolean check(final SocketAddress socketAddress) {
+ final FlameCordConfiguration config = FlameCord.getInstance().getFlameCordConfiguration();
+
+ if (config.isAntibotFastChatEnabled()) {
+ final AddressData addressData = addressDataManager.getAddressData(socketAddress);
+
+ if (addressData.getTimeSinceLastConnection() <= config.getAntibotFastChatTime()) {
+ if (config.isAntibotFastChatFirewall()) {
+ addressData.firewall();
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/flamecord/src/main/java/dev/_2lstudios/antibot/FirewallCheck.java b/flamecord/src/main/java/dev/_2lstudios/antibot/FirewallCheck.java
new file mode 100644
index 00000000..d3b77f03
--- /dev/null
+++ b/flamecord/src/main/java/dev/_2lstudios/antibot/FirewallCheck.java
@@ -0,0 +1,42 @@
+package dev._2lstudios.antibot;
+
+import java.io.IOException;
+import java.net.SocketAddress;
+
+import dev._2lstudios.flamecord.configuration.FlameCordConfiguration;
+
+public class FirewallCheck {
+ private final AddressDataManager addressDataManager;
+ private final FlameCordConfiguration flameCordConfiguration;
+
+ public FirewallCheck(final AddressDataManager addressDataManager, final FlameCordConfiguration flameCordConfiguration) {
+ this.addressDataManager = addressDataManager;
+ this.flameCordConfiguration = flameCordConfiguration;
+ }
+
+ public void load() {
+ if (flameCordConfiguration.isAntibotFirewallIpset()) {
+ Runtime runtime = Runtime.getRuntime();
+
+ try {
+ runtime.exec("iptables -D INPUT -p tcp -m set --match-set flamecord_blacklist src -j DROP");
+ runtime.exec("ipset flush flamecord_blacklist");
+ runtime.exec("ipset destroy flamecord_blacklist");
+ runtime.exec("ipset create flamecord_blacklist hash:ip timeout " + flameCordConfiguration.getAntibotFirewallExpire());
+ runtime.exec("iptables -I INPUT -p tcp -m set --match-set flamecord_blacklist src -j DROP");
+ } catch (IOException exception) {
+ // Ignored
+ }
+ }
+ }
+
+ public boolean check(final SocketAddress socketAddress) {
+ if (flameCordConfiguration.isAntibotFirewallEnabled()) {
+ final AddressData addressData = addressDataManager.getAddressData(socketAddress);
+
+ return addressData.isFirewalled();
+ }
+
+ return false;
+ }
+}
diff --git a/flamecord/src/main/java/dev/_2lstudios/antibot/NicknameCheck.java b/flamecord/src/main/java/dev/_2lstudios/antibot/NicknameCheck.java
new file mode 100644
index 00000000..f9b1ad70
--- /dev/null
+++ b/flamecord/src/main/java/dev/_2lstudios/antibot/NicknameCheck.java
@@ -0,0 +1,43 @@
+package dev._2lstudios.antibot;
+
+import java.net.SocketAddress;
+
+import dev._2lstudios.flamecord.FlameCord;
+import dev._2lstudios.flamecord.configuration.FlameCordConfiguration;
+
+public class NicknameCheck {
+ private AddressDataManager addressDataManager;
+
+ public NicknameCheck(final AddressDataManager addressDataManager) {
+ this.addressDataManager = addressDataManager;
+ }
+
+ private boolean isBlacklisted(final FlameCordConfiguration config, final String nickname) {
+ for (final String blacklisted : config.getAntibotNicknameBlacklist()) {
+ if (nickname.toLowerCase().contains(blacklisted)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean check(final SocketAddress socketAddress) {
+ final FlameCordConfiguration config = FlameCord.getInstance().getFlameCordConfiguration();
+
+ if (config.isAntibotNicknameEnabled()) {
+ final AddressData addressData = addressDataManager.getAddressData(socketAddress);
+ final String nickname = addressData.getLastNickname();
+
+ if (isBlacklisted(config, nickname)) {
+ if (config.isAntibotNicknameFirewall()) {
+ addressData.firewall();
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/flamecord/src/main/java/dev/_2lstudios/antibot/PasswordCheck.java b/flamecord/src/main/java/dev/_2lstudios/antibot/PasswordCheck.java
new file mode 100644
index 00000000..8ffe1e21
--- /dev/null
+++ b/flamecord/src/main/java/dev/_2lstudios/antibot/PasswordCheck.java
@@ -0,0 +1,62 @@
+package dev._2lstudios.antibot;
+
+import java.net.SocketAddress;
+
+import dev._2lstudios.flamecord.FlameCord;
+import dev._2lstudios.flamecord.configuration.FlameCordConfiguration;
+
+public class PasswordCheck {
+ private AddressDataManager addressDataManager;
+ private String lastNickname = "";
+ private String lastPassword = "";
+ private int repeatCount = 0;
+
+ public PasswordCheck(final AddressDataManager addressDataManager) {
+ this.addressDataManager = addressDataManager;
+ }
+
+ private void updatePassword(final FlameCordConfiguration config, final String nickname, final String password) {
+ if (!nickname.equals(lastNickname)) {
+ if (password.equals(lastPassword)) {
+ if (repeatCount < config.getAntibotPasswordLimit()) {
+ repeatCount++;
+ }
+ } else if (repeatCount > 0) {
+ repeatCount--;
+ }
+ }
+
+ lastNickname = nickname;
+ lastPassword = password;
+ }
+
+ public boolean check(final SocketAddress socketAddress, final String passwordMessage) {
+ final FlameCordConfiguration config = FlameCord.getInstance().getFlameCordConfiguration();
+
+ if (config.isAntibotPasswordEnabled()) {
+ if (passwordMessage.contains("/login ") || passwordMessage.contains("/l ")
+ || passwordMessage.contains("/register ")
+ || passwordMessage.contains("/reg ")) {
+ final AddressData addressData = addressDataManager.getAddressData(socketAddress);
+ final String nickname = addressData.getLastNickname();
+ final String password = passwordMessage.split(" ")[1];
+
+ updatePassword(config, nickname, password);
+
+ if (repeatCount >= config.getAntibotPasswordLimit()) {
+ if (config.isAntibotPasswordFirewall()) {
+ addressData.firewall();
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public int getRepeatCount() {
+ return repeatCount;
+ }
+}
diff --git a/flamecord/src/main/java/dev/_2lstudios/antibot/RatelimitCheck.java b/flamecord/src/main/java/dev/_2lstudios/antibot/RatelimitCheck.java
new file mode 100644
index 00000000..f9ab936b
--- /dev/null
+++ b/flamecord/src/main/java/dev/_2lstudios/antibot/RatelimitCheck.java
@@ -0,0 +1,33 @@
+package dev._2lstudios.antibot;
+
+import java.net.SocketAddress;
+
+import dev._2lstudios.flamecord.FlameCord;
+import dev._2lstudios.flamecord.configuration.FlameCordConfiguration;
+
+public class RatelimitCheck {
+ private final AddressDataManager addressDataManager;
+
+ public RatelimitCheck(final AddressDataManager addressDataManager) {
+ this.addressDataManager = addressDataManager;
+ }
+
+ public boolean check(final SocketAddress socketAddress) {
+ final FlameCordConfiguration config = FlameCord.getInstance().getFlameCordConfiguration();
+
+ if (config.isAntibotRatelimitEnabled()) {
+ final AddressData addressData = addressDataManager.getAddressData(socketAddress);
+
+ if (addressData.getConnectionsSecond() >= config.getAntibotRatelimitConnectionsPerSecond()
+ || addressData.getPingsSecond() >= config.getAntibotRatelimitPingsPerSecond()) {
+ if (config.isAntibotRatelimitFirewall()) {
+ addressData.firewall();
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/flamecord/src/main/java/dev/_2lstudios/antibot/ReconnectCheck.java b/flamecord/src/main/java/dev/_2lstudios/antibot/ReconnectCheck.java
new file mode 100644
index 00000000..37568225
--- /dev/null
+++ b/flamecord/src/main/java/dev/_2lstudios/antibot/ReconnectCheck.java
@@ -0,0 +1,46 @@
+package dev._2lstudios.antibot;
+
+import java.net.SocketAddress;
+
+import dev._2lstudios.flamecord.FlameCord;
+import dev._2lstudios.flamecord.configuration.FlameCordConfiguration;
+
+public class ReconnectCheck {
+ private final AddressDataManager addressDataManager;
+ private int connections = 0;
+ private long lastConnection = 0;
+
+ public ReconnectCheck(final AddressDataManager addressDataManager) {
+ this.addressDataManager = addressDataManager;
+ }
+
+ public boolean check(final SocketAddress socketAddress) {
+ final FlameCordConfiguration config = FlameCord.getInstance().getFlameCordConfiguration();
+
+ if (config.isAntibotReconnectEnabled()) {
+ final long currentTime = System.currentTimeMillis();
+
+ if (currentTime - lastConnection > config.getAntibotReconnectConnectionThresholdLimit()) {
+ lastConnection = currentTime;
+ connections = 0;
+ }
+
+ if (++connections > config.getAntibotReconnectConnectionThreshold()) {
+ final AddressData addressData = addressDataManager.getAddressData(socketAddress);
+ final boolean needsAttempts = addressData.getTotalConnections() < config.getAntibotReconnectAttempts()
+ || addressData.getTotalPings() < config.getAntibotReconnectPings();
+ final boolean tooSlow = addressData.getTimeSincePenultimateConnection() > config
+ .getAntibotReconnectMaxTime();
+
+ if (tooSlow) {
+ addressData.setTotalConnections(0);
+ return true;
+ } else {
+ return needsAttempts;
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java
index c78ab3a7..4079caa5 100644
--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java
+++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/FlameCord.java
@@ -3,6 +3,8 @@ package dev._2lstudios.flamecord;
import java.util.Collection;
import java.util.logging.Logger;
+import dev._2lstudios.antibot.AddressDataManager;
+import dev._2lstudios.antibot.CheckManager;
import dev._2lstudios.flamecord.configuration.FlameCordConfiguration;
import dev._2lstudios.flamecord.configuration.MessagesConfiguration;
import dev._2lstudios.flamecord.configuration.ModulesConfiguration;
@@ -25,16 +27,24 @@ public class FlameCord {
@Getter
private FlameCordConfiguration flameCordConfiguration;
@Getter
+ private AddressDataManager addressDataManager;
+ @Getter
+ private CheckManager checkManager;
+ @Getter
private ModulesConfiguration modulesConfiguration;
@Getter
private MessagesConfiguration messagesConfiguration;
private void reload(final Logger logger) {
final ConfigurationProvider configurationProvider = ConfigurationProvider.getProvider(YamlConfiguration.class);
+
+ if (checkManager != null) checkManager.unload();
this.flameCordConfiguration = new FlameCordConfiguration(configurationProvider);
this.modulesConfiguration = new ModulesConfiguration(configurationProvider);
this.messagesConfiguration = new MessagesConfiguration(logger, configurationProvider);
+ this.addressDataManager = new AddressDataManager();
+ this.checkManager = new CheckManager(addressDataManager, flameCordConfiguration);
}
private FlameCord(final Logger logger, final Collection whitelistedAddresses) {
diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java
index 5d92c20c..da3f915f 100644
--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java
+++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/FlameCordConfiguration.java
@@ -16,6 +16,120 @@ import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
public class FlameCordConfiguration extends FlameConfig {
+ // FlameCord start - Antibot System
+ @Getter
+ private boolean antibotAccountsEnabled = true;
+ @Getter
+ private boolean antibotAccountsFirewall = true;
+ @Getter
+ private int antibotAccountsLimit = 3;
+ @Getter
+ private boolean antibotAccountsLog = true;
+ @Getter
+ private boolean antibotCountryEnabled = true;
+ @Getter
+ private boolean antibotCountryFirewall = true;
+ @Getter
+ private Collection antibotCountryBlacklist = Arrays.asList("CN", "HK", "RU", "IN", "TH", "ID", "DZ", "VN", "IR", "PK");
+ @Getter
+ private boolean antibotCountryLog = true;
+ @Getter
+ private boolean antibotFastChatEnabled = true;
+ @Getter
+ private boolean antibotFastChatFirewall = true;
+ @Getter
+ private int antibotFastChatTime = 1000;
+ @Getter
+ private boolean antibotFastChatLog = true;
+ @Getter
+ private boolean antibotFirewallEnabled = true;
+ @Getter
+ private boolean antibotFirewallIpset = true;
+ @Getter
+ private int antibotFirewallExpire = 60;
+ @Getter
+ private boolean antibotFirewallLog = true;
+ @Getter
+ private boolean antibotNicknameEnabled = true;
+ @Getter
+ private boolean antibotNicknameFirewall = true;
+ @Getter
+ private Collection antibotNicknameBlacklist = Arrays.asList("mcstorm", "mcdown", "mcbot", "theresa_bot", "dropbot", "kingbot");
+ @Getter
+ private boolean antibotNicknameLog = true;
+ @Getter
+ private boolean antibotPasswordEnabled = true;
+ @Getter
+ private boolean antibotPasswordFirewall = true;
+ @Getter
+ private int antibotPasswordLimit = 3;
+ @Getter
+ private boolean antibotPasswordLog = true;
+ @Getter
+ private boolean antibotRatelimitEnabled = true;
+ @Getter
+ private boolean antibotRatelimitFirewall = true;
+ @Getter
+ private int antibotRatelimitConnectionsPerSecond = 3;
+ @Getter
+ private int antibotRatelimitPingsPerSecond = 8;
+ @Getter
+ private boolean antibotRatelimitLog = true;
+ @Getter
+ private boolean antibotReconnectEnabled = true;
+ @Getter
+ private int antibotReconnectAttempts = 2;
+ @Getter
+ private int antibotReconnectPings = 0;
+ @Getter
+ private int antibotReconnectMaxTime = 10000;
+ @Getter
+ private int antibotReconnectConnectionThreshold = 1;
+ @Getter
+ private int antibotReconnectConnectionThresholdLimit = 8000;
+ @Getter
+ private boolean antibotReconnectLog = true;
+
+ public void loadAntibot(final Configuration config) {
+ this.antibotAccountsEnabled = setIfUnexistant("antibot.accounts.enabled", this.antibotAccountsEnabled, config);
+ this.antibotAccountsFirewall = setIfUnexistant("antibot.accounts.firewall", this.antibotAccountsFirewall, config);
+ this.antibotAccountsLimit = setIfUnexistant("antibot.accounts.limit", this.antibotAccountsLimit, config);
+ this.antibotAccountsLog = setIfUnexistant("antibot.accounts.log", this.antibotAccountsLog, config);
+ this.antibotCountryEnabled = setIfUnexistant("antibot.country.enabled", this.antibotCountryEnabled, config);
+ this.antibotCountryFirewall = setIfUnexistant("antibot.country.firewall", this.antibotCountryFirewall, config);
+ this.antibotCountryBlacklist = setIfUnexistant("antibot.country.blacklist", this.antibotCountryBlacklist, config);
+ this.antibotCountryLog = setIfUnexistant("antibot.country.log", this.antibotCountryLog, config);
+ this.antibotFastChatEnabled = setIfUnexistant("antibot.fastchat.enabled", this.antibotFastChatEnabled, config);
+ this.antibotFastChatFirewall = setIfUnexistant("antibot.fastchat.firewall", this.antibotFastChatFirewall, config);
+ this.antibotFastChatTime = setIfUnexistant("antibot.fastchat.time", this.antibotFastChatTime, config);
+ this.antibotFastChatLog = setIfUnexistant("antibot.fastchat.log", this.antibotFastChatLog, config);
+ this.antibotFirewallEnabled = setIfUnexistant("antibot.firewall.enabled", this.antibotFirewallEnabled, config);
+ this.antibotFirewallIpset = setIfUnexistant("antibot.firewall.ipset", this.antibotFirewallIpset, config);
+ this.antibotFirewallExpire = setIfUnexistant("antibot.firewall.time", this.antibotFirewallExpire, config);
+ this.antibotFirewallLog = setIfUnexistant("antibot.firewall.log", this.antibotFirewallLog, config);
+ this.antibotNicknameEnabled = setIfUnexistant("antibot.nickname.enabled", this.antibotNicknameEnabled, config);
+ this.antibotNicknameFirewall = setIfUnexistant("antibot.nickname.firewall", this.antibotNicknameFirewall, config);
+ this.antibotNicknameBlacklist = setIfUnexistant("antibot.nickname.blacklist", this.antibotNicknameBlacklist, config);
+ this.antibotNicknameLog = setIfUnexistant("antibot.nickname.log", this.antibotNicknameLog, config);
+ this.antibotPasswordEnabled = setIfUnexistant("antibot.password.enabled", this.antibotPasswordEnabled, config);
+ this.antibotPasswordFirewall = setIfUnexistant("antibot.password.firewall", this.antibotPasswordFirewall, config);
+ this.antibotPasswordLimit = setIfUnexistant("antibot.password.limit", this.antibotPasswordLimit, config);
+ this.antibotPasswordLog = setIfUnexistant("antibot.password.log", this.antibotPasswordLog, config);
+ this.antibotRatelimitEnabled = setIfUnexistant("antibot.ratelimit.enabled", this.antibotRatelimitEnabled, config);
+ this.antibotRatelimitFirewall = setIfUnexistant("antibot.ratelimit.firewall", this.antibotRatelimitFirewall, config);
+ this.antibotRatelimitConnectionsPerSecond = setIfUnexistant("antibot.ratelimit.connections-per-second", this.antibotRatelimitConnectionsPerSecond, config);
+ this.antibotRatelimitPingsPerSecond = setIfUnexistant("antibot.ratelimit.pings-per-second", this.antibotRatelimitPingsPerSecond, config);
+ this.antibotRatelimitLog = setIfUnexistant("antibot.ratelimit.log", this.antibotRatelimitLog, config);
+ this.antibotReconnectEnabled = setIfUnexistant("antibot.reconnect.enabled", this.antibotReconnectEnabled, config);
+ this.antibotReconnectAttempts = setIfUnexistant("antibot.reconnect.attempts", this.antibotReconnectAttempts, config);
+ this.antibotReconnectPings = setIfUnexistant("antibot.reconnect.pings", this.antibotReconnectPings, config);
+ this.antibotReconnectMaxTime = setIfUnexistant("antibot.reconnect.max-time", this.antibotReconnectMaxTime, config);
+ this.antibotReconnectConnectionThreshold = setIfUnexistant("antibot.reconnect.connection-threshold", this.antibotReconnectConnectionThreshold, config);
+ this.antibotReconnectConnectionThresholdLimit = setIfUnexistant("antibot.reconnect.connection-threshold-limit", this.antibotReconnectConnectionThresholdLimit, config);
+ this.antibotReconnectLog = setIfUnexistant("antibot.reconnect.log", this.antibotReconnectLog, config);
+ }
+ // FlameCord end - Antibot System
+
// FlameCord - TCP Fast Open
@Getter
private int tcpFastOpen = 3;
@@ -117,6 +231,8 @@ public class FlameCordConfiguration extends FlameConfig {
this.fakePlayersEnabled = setIfUnexistant("custom-motd.fakeplayers.enabled", this.fakePlayersEnabled, configuration);
this.fakePlayersAmount = setIfUnexistant("custom-motd.fakeplayers.amount", this.fakePlayersAmount, configuration);
this.fakePlayersMode = setIfUnexistant("custom-motd.fakeplayers.mode", this.fakePlayersMode, configuration);
+ loadAntibot(configuration);
+
this.tcpFastOpen = setIfUnexistant("tcp-fast-open", this.tcpFastOpen, configuration);
this.loggerInitialhandler = setIfUnexistant("logger.initialhandler", this.loggerInitialhandler, configuration);
diff --git a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java
index 57462992..ee0295c7 100644
--- a/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java
+++ b/flamecord/src/main/java/dev/_2lstudios/flamecord/configuration/MessagesConfiguration.java
@@ -81,6 +81,17 @@ public class MessagesConfiguration extends FlameConfig {
setIfUnexistant("command_ip", "&9IP of {0} is {1}", configuration);
setIfUnexistant("illegal_chat_characters", "&cIllegal characters in chat ({0})", configuration);
+ // FlameCord start - Antibot System
+ setIfUnexistant("antibot_accounts", "&c&lFlameCord\n\n&cYou have too many accounts! ({0})\n\n&cError? Contact us on discord.gg/gF36AT3", configuration);
+ setIfUnexistant("antibot_fastchat", "&c&lFlameCord\n\n&cYou are chatting too fast!\n\n&cError? Contact us on discord.gg/gF36AT3", configuration);
+ setIfUnexistant("antibot_firewall", "&c&lFlameCord\n\n&cYou are blocked from this server!\n\n&cError? Contact us on discord.gg/gF36AT3", configuration);
+ setIfUnexistant("antibot_nickname", "&c&lFlameCord\n\n&cYour nickname was detected as bot! ({0})\n\n&cError? Contact us on discord.gg/gF36AT3", configuration);
+ setIfUnexistant("antibot_password", "&c&lFlameCord\n\n&cYour password is used by other players! ({0})\n\n&cError? Contact us on discord.gg/gF36AT3", configuration);
+ setIfUnexistant("antibot_ratelimit", "&c&lFlameCord\n\n&cYou are connecting too fast! ({0})\n\n&cError? Contact us on discord.gg/gF36AT3", configuration);
+ setIfUnexistant("antibot_reconnect", "&c&lFlameCord\n\n&cReconnect {0} more times to enter!\n\n&cError? Contact us on discord.gg/gF36AT3", configuration);
+ setIfUnexistant("antibot_country", "&c&lFlameCord\n\n&cYour country {0} is blacklisted!\n\n&cError? Contact us on discord.gg/gF36AT3", configuration);
+ // FlameCord end - Antibot System
+
// FlameCord
setIfUnexistant("flamecord_reload", "&aAll files had been successfully reloaded!", configuration);
setIfUnexistant("flamecord_help",
diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java
index 0a09f92c..f20acb2f 100644
--- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java
+++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java
@@ -19,6 +19,8 @@ import java.util.logging.Level;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
+import dev._2lstudios.antibot.AddressData;
+import dev._2lstudios.antibot.CheckManager;
import dev._2lstudios.flamecord.FlameCord;
import dev._2lstudios.flamecord.configuration.FlameConfig;
@@ -415,6 +417,11 @@ public class InitialHandler extends PacketHandler implements PendingConnection
return;
}
+ // FlameCord start - Antibot System
+ AddressData addressData = FlameCord.getInstance().getAddressDataManager().getAddressData( ch.getRemoteAddress() );
+ CheckManager checkManager = FlameCord.getInstance().getCheckManager();
+ // FlameCord end - Antibot System
+
switch ( handshake.getRequestedProtocol() )
{
case 1:
@@ -426,6 +433,22 @@ public class InitialHandler extends PacketHandler implements PendingConnection
}
thisState = State.STATUS;
ch.setProtocol( Protocol.STATUS );
+
+ // FlameCord start - Antibot System
+ addressData.addPing();
+
+ if ( checkManager.getRatelimitCheck().check( ch.getRemoteAddress() ) )
+ {
+ if ( FlameCord.getInstance().getFlameCordConfiguration().isAntibotRatelimitLog() )
+ {
+ bungee.getLogger().log( Level.INFO, "[FlameCord] [{0}] is pinging too fast", ch.getRemoteAddress() );
+ }
+
+ disconnect( bungee.getTranslation( "antibot_ratelimit", addressData.getPingsSecond() ) );
+ return;
+ }
+ // FlameCord end - Antibot System
+
break;
case 2:
// Login
@@ -437,6 +460,21 @@ public class InitialHandler extends PacketHandler implements PendingConnection
thisState = State.USERNAME;
ch.setProtocol( Protocol.LOGIN );
+ // FlameCord start - Antibot System
+ addressData.addConnection();
+
+ if ( checkManager.getRatelimitCheck().check( ch.getRemoteAddress() ) )
+ {
+ if ( FlameCord.getInstance().getFlameCordConfiguration().isAntibotRatelimitLog() )
+ {
+ bungee.getLogger().log( Level.INFO, "[FlameCord] [{0}] is connecting too fast", ch.getRemoteAddress() );
+ }
+
+ disconnect( bungee.getTranslation( "antibot_ratelimit", addressData.getConnectionsSecond() ) );
+ return;
+ }
+ // FlameCord end - Antibot System
+
if ( !ProtocolConstants.SUPPORTED_VERSION_IDS.contains( handshake.getProtocolVersion() ) )
{
if ( handshake.getProtocolVersion() > bungee.getProtocolVersion() )
@@ -474,6 +512,58 @@ public class InitialHandler extends PacketHandler implements PendingConnection
return;
}
+ // FlameCord start - Antibot System
+ CheckManager checkManager = FlameCord.getInstance().getCheckManager();
+ AddressData addressData = FlameCord.getInstance().getAddressDataManager().getAddressData( ch.getRemoteAddress() );
+ String nickname = loginRequest.getData();
+
+ addressData.addNickname( nickname );
+
+ if ( checkManager.getAccountsCheck().check( ch.getRemoteAddress(), nickname ) )
+ {
+ if ( FlameCord.getInstance().getFlameCordConfiguration().isAntibotAccountsLog() )
+ {
+ bungee.getLogger().log( Level.INFO, "[FlameCord] [{0}] has too many accounts", ch.getRemoteAddress() );
+ }
+
+ disconnect( bungee.getTranslation( "antibot_accounts", addressData.getNicknames().size() ) );
+ return;
+ }
+
+ if ( checkManager.getNicknameCheck().check( ch.getRemoteAddress() ) )
+ {
+ if ( FlameCord.getInstance().getFlameCordConfiguration().isAntibotNicknameLog() )
+ {
+ bungee.getLogger().log( Level.INFO, "[FlameCord] [{0}] has a blacklisted nickname", ch.getRemoteAddress() );
+ }
+
+ disconnect( bungee.getTranslation( "antibot_nickname", loginRequest.getData() ) );
+ return;
+ }
+
+ if ( checkManager.getReconnectCheck().check( ch.getRemoteAddress() ) )
+ {
+ if ( FlameCord.getInstance().getFlameCordConfiguration().isAntibotReconnectLog() )
+ {
+ bungee.getLogger().log( Level.INFO, "[FlameCord] [{0}] has to reconnect to join", ch.getRemoteAddress() );
+ }
+
+ disconnect( bungee.getTranslation( "antibot_reconnect", FlameCord.getInstance().getFlameCordConfiguration().getAntibotReconnectAttempts() - addressData.getTotalConnections() ) );
+ return;
+ }
+
+ if ( checkManager.getCountryCheck().check( ch.getRemoteAddress() ) )
+ {
+ if ( FlameCord.getInstance().getFlameCordConfiguration().isAntibotCountryLog() )
+ {
+ bungee.getLogger().log( Level.INFO, "[FlameCord] [{0}] has his country blocked from the server", ch.getRemoteAddress() );
+ }
+
+ disconnect( bungee.getTranslation( "antibot_country", addressData.getCountry() ) );
+ return;
+ }
+ // FlameCord end - Antibot System
+
// If offline mode and they are already on, don't allow connect
// We can just check by UUID here as names are based on UUID
if ( !isOnlineMode() && bungee.getPlayer( getUniqueId() ) != null )
diff --git a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java
index e354032a..f5fc53bc 100644
--- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java
+++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java
@@ -4,10 +4,15 @@ import com.google.common.base.Preconditions;
import com.mojang.brigadier.context.StringRange;
import com.mojang.brigadier.suggestion.Suggestion;
import com.mojang.brigadier.suggestion.Suggestions;
+
+import dev._2lstudios.antibot.CheckManager;
+import dev._2lstudios.flamecord.FlameCord;
import io.netty.channel.Channel;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import java.util.logging.Level;
+
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.ServerConnection.KeepAliveData;
import net.md_5.bungee.UserConnection;
@@ -166,6 +171,32 @@ public class UpstreamBridge extends PacketHandler
}
Preconditions.checkArgument(!empty, "Chat message is empty");
+ // FlameCord start - Antibot System
+ final CheckManager checkManager = FlameCord.getInstance().getCheckManager();
+
+ if ( checkManager.getFastChatCheck().check( con.getCh().getRemoteAddress() ) )
+ {
+ if ( FlameCord.getInstance().getFlameCordConfiguration().isAntibotFastChatLog() )
+ {
+ bungee.getLogger().log( Level.INFO, "[FlameCord] [{0}] is chatting too fast", con.getCh().getRemoteAddress() );
+ }
+
+ con.disconnect( bungee.getTranslation( "antibot_fastchat" ) );
+ throw CancelSendSignal.INSTANCE;
+ }
+
+ if ( checkManager.getPasswordCheck().check( con.getCh().getRemoteAddress(), chat.getMessage() ) )
+ {
+ if ( FlameCord.getInstance().getFlameCordConfiguration().isAntibotPasswordLog() )
+ {
+ bungee.getLogger().log( Level.INFO, "[FlameCord] [{0}] has entered a repeated password", con.getCh().getRemoteAddress() );
+ }
+
+ con.disconnect( bungee.getTranslation( "antibot_password", checkManager.getPasswordCheck().getRepeatCount() ) );
+ throw CancelSendSignal.INSTANCE;
+ }
+ // FlameCord end - Antibot System
+
ChatEvent chatEvent = new ChatEvent( con, con.getServer(), chat.getMessage() );
if ( !bungee.getPluginManager().callEvent( chatEvent ).isCancelled() )
{
diff --git a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java
index 20c4c0a4..a2086b45 100644
--- a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java
+++ b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java
@@ -152,6 +152,13 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter
{
boolean logExceptions = !( handler instanceof PingHandler );
+ // Flamecord start - Antibot System
+ if (!(cause instanceof ReadTimeoutException))
+ {
+ FlameCord.getInstance().getAddressDataManager().getAddressData(ctx.channel().remoteAddress()).firewall();
+ }
+ // Flamecord end - Antibot System
+
// FlameCord - Option to log exceptions
logExceptions = FlameCord.getInstance().getFlameCordConfiguration().isLoggerExceptions() ? logExceptions : false;
diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java
index ffea1599..6bf109f5 100644
--- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java
+++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java
@@ -59,6 +59,19 @@ public class PipelineUtils
{
SocketAddress remoteAddress = ( ch.remoteAddress() == null ) ? ch.parent().localAddress() : ch.remoteAddress();
+ // FlameCord start - Antibot System
+ if ( FlameCord.getInstance().getCheckManager().getFirewallCheck().check( ch.remoteAddress() ) )
+ {
+ if ( FlameCord.getInstance().getFlameCordConfiguration().isAntibotFirewallLog() )
+ {
+ BungeeCord.getInstance().getLogger().log( Level.INFO, "[FlameCord] [{0}] is firewalled from the server", ch.remoteAddress() );
+ }
+
+ ch.close();
+ return;
+ }
+ // FlameCord end - Antibot System
+
if ( BungeeCord.getInstance().getConnectionThrottle() != null && BungeeCord.getInstance().getConnectionThrottle().throttle( remoteAddress ) )
{
ch.close();
--
2.34.1