diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitConfig.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitConfig.java index b342bb2..d7b093c 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitConfig.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitConfig.java @@ -10,15 +10,31 @@ public class BukkitConfig implements VPNConfig { private final ConfigDefault licenseDefault = new ConfigDefault<>("", "license", BukkitPlugin.pluginInstance), kickStringDefault = new ConfigDefault<>("Proxies are not allowed on our server", - "kickMessage", BukkitPlugin.pluginInstance); + "kickMessage", BukkitPlugin.pluginInstance), + defaultDatabaseType = new ConfigDefault<>("MySQL", + "database.type", BukkitPlugin.pluginInstance), + defaultDatabaseName = new ConfigDefault<>("kaurivpn", + "database.database", BukkitPlugin.pluginInstance), + defaultUsername = new ConfigDefault<>("root", + "database.username", BukkitPlugin.pluginInstance), + defaultPassword = new ConfigDefault<>("password", + "database.password", BukkitPlugin.pluginInstance), + defaultAuthDatabase = new ConfigDefault<>("admin", + "database.auth", BukkitPlugin.pluginInstance), + defaultIp = new ConfigDefault<>("localhost", "database.ip", BukkitPlugin.pluginInstance); private final ConfigDefault cacheResultsDefault = new ConfigDefault<>(true, - "cachedResults", BukkitPlugin.pluginInstance); + "cachedResults", BukkitPlugin.pluginInstance), + defaultDatabaseEnabled = new ConfigDefault<>(false, "database.enabled", + BukkitPlugin.pluginInstance); + private final ConfigDefault + defaultPort = new ConfigDefault<>(-1, "database.port", BukkitPlugin.pluginInstance); private final ConfigDefault> prefixWhitelistsDefault = new ConfigDefault<>(new ArrayList<>(), "prefixWhitelists", BukkitPlugin.pluginInstance); - private String license, kickMessage; + private String license, kickMessage, databaseType, databaseName, username, password, ip; private List prefixWhitelists; - private boolean cacheResults; + private int port; + private boolean cacheResults, databaseEnabled; @Override public String getLicense() { @@ -40,10 +56,64 @@ public class BukkitConfig implements VPNConfig { return prefixWhitelists; } + @Override + public boolean isDatabaseEnabled() { + return databaseEnabled; + } + + @Override + public String getDatabaseType() { + return databaseType; + } + + @Override + public String getDatabaseName() { + return databaseName; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getIp() { + return ip; + } + + @Override + public int getPort() { + if(port == -1) { + switch (getDatabaseType().toLowerCase()) { + case "mongodb": + case "mongo": + case "mongod": + return 27017; + case "sql": + case "mysql": + return 3306; + } + } + + return port; + } + public void update() { license = licenseDefault.get(); kickMessage = kickStringDefault.get(); cacheResults = cacheResultsDefault.get(); prefixWhitelists = prefixWhitelistsDefault.get(); + databaseEnabled = defaultDatabaseEnabled.get(); + databaseType = defaultDatabaseType.get(); + databaseName = defaultDatabaseName.get(); + username = defaultUsername.get(); + password = defaultPassword.get(); + ip = defaultIp.get(); + port = defaultPort.get(); } } diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java index 743a62e..2749b60 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java @@ -43,6 +43,8 @@ public class BukkitListener extends VPNExecutor implements Listener { @EventHandler public void onListener(final AsyncPlayerPreLoginEvent event) { + //If they're exempt, don't check. + if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getUniqueId())) return; checkIp(event.getAddress().getHostAddress(), AntiVPN.getInstance().getConfig().cachedResults(), result -> { if(result.isSuccess() && result.isProxy()) { event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER); diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlayerExecutor.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlayerExecutor.java index 7b6d264..608d843 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlayerExecutor.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlayerExecutor.java @@ -3,6 +3,7 @@ package dev.brighten.antivpn.bukkit; import dev.brighten.antivpn.api.APIPlayer; import dev.brighten.antivpn.api.PlayerExecutor; import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import java.util.List; @@ -11,6 +12,7 @@ import java.util.UUID; import java.util.stream.Collectors; public class BukkitPlayerExecutor implements PlayerExecutor { + @Override public Optional getPlayer(String name) { final Player player = Bukkit.getPlayer(name); @@ -33,8 +35,10 @@ public class BukkitPlayerExecutor implements PlayerExecutor { return Optional.of(new BukkitPlayer(player)); } + @Override public List getOnlinePlayers() { return Bukkit.getOnlinePlayers().stream().map(BukkitPlayer::new).collect(Collectors.toList()); } + } diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java index cf8f6c4..6a9dee1 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java @@ -56,7 +56,8 @@ public class BukkitPlugin extends JavaPlugin { if(children.length > 0 && args.length > 0) { for (Command child : children) { - if(child.name().equalsIgnoreCase(args[0])) { + if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases()) + .anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) { return child.tabComplete(new BukkitCommandExecutor(sender), alias, IntStream .range(0, args.length - 1) .mapToObj(i -> args[i + 1]).toArray(String[]::new)); @@ -78,7 +79,8 @@ public class BukkitPlugin extends JavaPlugin { if(children.length > 0 && args.length > 0) { for (Command child : children) { - if(child.name().equalsIgnoreCase(args[0])) { + if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases()) + .anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) { if(!sender.hasPermission("antivpn.command.*") && !sender.hasPermission(child.permission())) { sender.sendMessage(ChatColor.RED + "No permission."); diff --git a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeConfig.java b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeConfig.java index adb00b6..67f21af 100644 --- a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeConfig.java +++ b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeConfig.java @@ -10,15 +10,31 @@ public class BungeeConfig implements VPNConfig { ; private final ConfigDefault licenseDefault = new ConfigDefault<>("", "license", BungeePlugin.pluginInstance), kickStringDefault = new ConfigDefault<>("Proxies are not allowed on our server", - "kickMessage", BungeePlugin.pluginInstance); + "kickMessage", BungeePlugin.pluginInstance), + defaultDatabaseType = new ConfigDefault<>("MySQL", + "database.type", BungeePlugin.pluginInstance), + defaultDatabaseName = new ConfigDefault<>("kaurivpn", + "database.database", BungeePlugin.pluginInstance), + defaultUsername = new ConfigDefault<>("root", + "database.username", BungeePlugin.pluginInstance), + defaultPassword = new ConfigDefault<>("password", + "database.password", BungeePlugin.pluginInstance), + defaultAuthDatabase = new ConfigDefault<>("admin", + "database.auth", BungeePlugin.pluginInstance), + defaultIp = new ConfigDefault<>("localhost", "database.ip", BungeePlugin.pluginInstance); private final ConfigDefault cacheResultsDefault = new ConfigDefault<>(true, - "cachedResults", BungeePlugin.pluginInstance); + "cachedResults", BungeePlugin.pluginInstance), + defaultDatabaseEnabled = new ConfigDefault<>(false, "database.enabled", + BungeePlugin.pluginInstance); + private final ConfigDefault + defaultPort = new ConfigDefault<>(-1, "database.port", BungeePlugin.pluginInstance); private final ConfigDefault> prefixWhitelistsDefault = new ConfigDefault<>(new ArrayList<>(), "prefixWhitelists", BungeePlugin.pluginInstance); - private String license, kickMessage; + private String license, kickMessage, databaseType, databaseName, username, password, ip; private List prefixWhitelists; - private boolean cacheResults; + private int port; + private boolean cacheResults, databaseEnabled; @Override public String getLicense() { @@ -40,10 +56,64 @@ public class BungeeConfig implements VPNConfig { ; return prefixWhitelists; } + @Override + public boolean isDatabaseEnabled() { + return databaseEnabled; + } + + @Override + public String getDatabaseType() { + return databaseType; + } + + @Override + public String getDatabaseName() { + return databaseName; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getIp() { + return ip; + } + + @Override + public int getPort() { + if(port == -1) { + switch (getDatabaseType().toLowerCase()) { + case "mongodb": + case "mongo": + case "mongod": + return 27017; + case "sql": + case "mysql": + return 3306; + } + } + + return port; + } + public void update() { license = licenseDefault.get(); kickMessage = kickStringDefault.get(); cacheResults = cacheResultsDefault.get(); prefixWhitelists = prefixWhitelistsDefault.get(); + databaseEnabled = defaultDatabaseEnabled.get(); + databaseType = defaultDatabaseType.get(); + databaseName = defaultDatabaseName.get(); + username = defaultUsername.get(); + password = defaultPassword.get(); + ip = defaultIp.get(); + port = defaultPort.get(); } } diff --git a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java index 3a8641f..542b851 100644 --- a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java +++ b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java @@ -43,6 +43,7 @@ public class BungeeListener extends VPNExecutor implements Listener { @EventHandler public void onListener(final PostLoginEvent event) { if(event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission + || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt //Or has a name that starts with a certain prefix. This is for Bedrock exempting. || AntiVPN.getInstance().getConfig().getPrefixWhitelists().stream() .anyMatch(prefix -> event.getPlayer().getName().startsWith(prefix))) return; diff --git a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlugin.java b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlugin.java index 9556d2e..5567cd0 100644 --- a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlugin.java +++ b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlugin.java @@ -13,6 +13,7 @@ import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.plugin.Plugin; +import java.util.Arrays; import java.util.stream.IntStream; public class BungeePlugin extends Plugin { @@ -52,7 +53,8 @@ public class BungeePlugin extends Plugin { if(children.length > 0 && args.length > 0) { for (Command child : children) { - if(child.name().equalsIgnoreCase(args[0])) { + if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases()) + .anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) { if(!sender.hasPermission("antivpn.command.*") && !sender.hasPermission(child.permission())) { sender.sendMessage(noPermission); diff --git a/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java b/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java index b0f98ee..554cd9c 100644 --- a/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java +++ b/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java @@ -6,6 +6,8 @@ import dev.brighten.antivpn.api.VPNExecutor; import dev.brighten.antivpn.command.Command; import dev.brighten.antivpn.command.impl.AntiVPNCommand; import dev.brighten.antivpn.command.impl.LookupCommand; +import dev.brighten.antivpn.database.VPNDatabase; +import dev.brighten.antivpn.database.sql.MySqlVPN; import dev.brighten.antivpn.utils.VPNResponse; import dev.brighten.antivpn.utils.json.JSONException; import dev.brighten.antivpn.utils.json.JSONObject; @@ -26,6 +28,7 @@ public class AntiVPN { private VPNConfig config; private VPNExecutor executor; private PlayerExecutor playerExecutor; + private VPNDatabase database; private List commands = new ArrayList<>(); public static void start(VPNConfig config, VPNExecutor executor, PlayerExecutor playerExecutor) { @@ -40,12 +43,34 @@ public class AntiVPN { INSTANCE.executor.registerListeners(); INSTANCE.config.update(); + switch(INSTANCE.config.getDatabaseType().toLowerCase()) { + case "mysql": + case "sql":{ + System.out.println("Using databaseType MySQL..."); + INSTANCE.database = new MySqlVPN(); + INSTANCE.database.init(); + break; + } + case "mongo": + case "mongodb": + case "mongod": { + System.out.println("We currently do not support Mongo, but this is coming in future updates."); + break; + } + default: { + System.out.println("Could not find database type \"" + INSTANCE.config.getDatabaseType() + "\". " + + "Options: [MySQL]"); + break; + } + } + //Registering commands INSTANCE.registerCommands(); } public void stop() { executor.shutdown(); + if(database != null) database.shutdown(); } public static AntiVPN getInstance() { @@ -65,6 +90,5 @@ public class AntiVPN { private void registerCommands() { commands.add(new AntiVPNCommand()); - commands.add(new LookupCommand()); } } diff --git a/Common/src/main/java/dev/brighten/antivpn/api/VPNConfig.java b/Common/src/main/java/dev/brighten/antivpn/api/VPNConfig.java index c6c6932..e27e78a 100644 --- a/Common/src/main/java/dev/brighten/antivpn/api/VPNConfig.java +++ b/Common/src/main/java/dev/brighten/antivpn/api/VPNConfig.java @@ -12,6 +12,20 @@ public interface VPNConfig { List getPrefixWhitelists(); + boolean isDatabaseEnabled(); + + String getDatabaseType(); + + String getDatabaseName(); + + String getUsername(); + + String getPassword(); + + String getIp(); + + int getPort(); + void update(); } diff --git a/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java b/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java index b4a3321..9439ea4 100644 --- a/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java +++ b/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java @@ -3,18 +3,21 @@ package dev.brighten.antivpn.api; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.utils.VPNResponse; import dev.brighten.antivpn.utils.json.JSONException; +import lombok.Getter; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; public abstract class VPNExecutor { public static ExecutorService threadExecutor = Executors.newSingleThreadExecutor(); private static final Map responseCache = new HashMap<>(); + @Getter + private final Set whitelisted = Collections.synchronizedSet(new HashSet<>()); public abstract void registerListeners(); @@ -26,13 +29,29 @@ public abstract class VPNExecutor { public abstract void shutdown(); + public boolean isWhitelisted(UUID uuid) { + return whitelisted.contains(uuid); + } + public void checkIp(String ip, boolean cachedResults, Consumer result) { threadExecutor.execute(() -> result.accept(responseCache.compute(ip, (key, val) -> { if(val == null) { - try { - return AntiVPN.getVPNResponse(ip, AntiVPN.getInstance().getConfig().getLicense(), cachedResults); - } catch (JSONException | IOException e) { - e.printStackTrace(); + Optional cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(ip); + + if(cachedRes.isPresent()) return cachedRes.get(); + else { + try { + VPNResponse response = AntiVPN + .getVPNResponse(ip, AntiVPN.getInstance().getConfig().getLicense(), cachedResults); + + if(response.isSuccess()) { + AntiVPN.getInstance().getDatabase().cacheResponse(response); + } + + return response; + } catch (JSONException | IOException e) { + e.printStackTrace(); + } } } @@ -43,10 +62,22 @@ public abstract class VPNExecutor { public VPNResponse checkIp(String ip, boolean cachedResults) { return responseCache.compute(ip, (key, val) -> { if(val == null) { - try { - return AntiVPN.getVPNResponse(ip, AntiVPN.getInstance().getConfig().getLicense(), cachedResults); - } catch (JSONException | IOException e) { - e.printStackTrace(); + Optional cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(ip); + + if(cachedRes.isPresent()) return cachedRes.get(); + else { + try { + VPNResponse response = AntiVPN + .getVPNResponse(ip, AntiVPN.getInstance().getConfig().getLicense(), cachedResults); + + if(response.isSuccess()) { + threadExecutor.execute(() -> AntiVPN.getInstance().getDatabase().cacheResponse(response)); + } + + return response; + } catch (JSONException | IOException e) { + e.printStackTrace(); + } } } diff --git a/Common/src/main/java/dev/brighten/antivpn/command/impl/AllowlistCommand.java b/Common/src/main/java/dev/brighten/antivpn/command/impl/AllowlistCommand.java new file mode 100644 index 0000000..534edd1 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/command/impl/AllowlistCommand.java @@ -0,0 +1,113 @@ +package dev.brighten.antivpn.command.impl; + +import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.api.APIPlayer; +import dev.brighten.antivpn.command.Command; +import dev.brighten.antivpn.command.CommandExecutor; + +import java.util.*; +import java.util.stream.Collectors; + +public class AllowlistCommand extends Command { + + private static final String[] secondArgs = new String[] {"add", "remove"}; + + @Override + public String permission() { + return "anticheat.command.allowlist"; + } + + @Override + public String name() { + return "allowlist"; + } + + @Override + public String[] aliases() { + return new String[] {"whitelist"}; + } + + @Override + public String description() { + return "Add/remove players to/from exemption list."; + } + + @Override + public String usage() { + return " "; + } + + @Override + public String parent() { + return "antivpn"; + } + + @Override + public Command[] children() { + return new Command[0]; + } + + @Override + public String execute(CommandExecutor executor, String[] args) { + if(args.length == 0 || Arrays.stream(secondArgs).noneMatch(arg -> arg.equalsIgnoreCase(args[0]))) { + return "&cUsage: /antivpn allowlist "; + } + + if(args.length == 1) + return "&cYou have to provide a player to allow or deny exemption."; + + boolean databaseEnabled = AntiVPN.getInstance().getConfig().isDatabaseEnabled(); + + if(!databaseEnabled) executor.sendMessage("&cThe database is currently not setup, " + + "so any changes here will disappear after a restart."); + + UUID uuid = null; + try { + uuid = UUID.fromString(args[1]); + } catch(IllegalArgumentException e) { + Optional player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(args[1]); + + if(!player.isPresent()) { + return "&cThe player \"" + args[1] + "\" is not online, so please provide a UUID."; + } + + uuid = player.get().getUuid(); + } + + if(!databaseEnabled) { + if(args[0].equalsIgnoreCase("add")) { + AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid); + return String.format("&aAdded &6%s &auuid to the exemption allowlist.", uuid.toString()); + } else { + AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid); + return String.format("&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString()); + } + } else { + if(args[0].equalsIgnoreCase("add")) { + AntiVPN.getInstance().getDatabase().setWhitelisted(uuid, true); + return String.format("&aAdded &6%s &auuid to the exemption allowlist.", uuid.toString()); + } else { + AntiVPN.getInstance().getDatabase().setWhitelisted(uuid, false); + return String.format("&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString()); + } + } + } + + @Override + public List tabComplete(CommandExecutor executor, String alias, String[] args) { + switch(args.length) { + case 1: { + return Arrays.stream(args) + .filter(narg -> narg.startsWith(args[0])) + .collect(Collectors.toList()); + } + case 2: { + return AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() + .map(APIPlayer::getName) + .filter(name -> name.toLowerCase().startsWith(args[1].toLowerCase())) + .collect(Collectors.toList()); + } + } + return Collections.emptyList(); + } +} diff --git a/Common/src/main/java/dev/brighten/antivpn/command/impl/AntiVPNCommand.java b/Common/src/main/java/dev/brighten/antivpn/command/impl/AntiVPNCommand.java index 738be74..b4794f4 100644 --- a/Common/src/main/java/dev/brighten/antivpn/command/impl/AntiVPNCommand.java +++ b/Common/src/main/java/dev/brighten/antivpn/command/impl/AntiVPNCommand.java @@ -4,7 +4,6 @@ import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.command.Command; import dev.brighten.antivpn.command.CommandExecutor; import dev.brighten.antivpn.utils.StringUtil; -import jdk.nashorn.internal.lookup.Lookup; import java.util.*; import java.util.stream.Collectors; @@ -42,7 +41,7 @@ public class AntiVPNCommand extends Command { @Override public Command[] children() { - return new Command[] {new LookupCommand()}; + return new Command[] {new LookupCommand(), new AllowlistCommand()}; } @Override @@ -52,11 +51,17 @@ public class AntiVPNCommand extends Command { messages.add(StringUtil.line("&8")); messages.add("&6&lAntiVPN Help Page"); messages.add(""); - for (Command child : AntiVPN.getInstance().getCommands()) { + for (Command cmd : AntiVPN.getInstance().getCommands()) { + messages.add(String.format("&8/&f%s &8- &7&o%s", "&7" + cmd.parent() + + (cmd.parent().length() > 0 ? " " : "") + "&f" + cmd.name() + " &7" + + cmd.usage(), description())); + } + for (Command child : children()) { messages.add(String.format("&8/&f%s &8- &7&o%s", "&7" + child.parent() + (child.parent().length() > 0 ? " " : "") + "&f" + child.name() + " &7" - + child.usage(), description())); + + child.usage(), description())); } + messages.add(StringUtil.line("&8")); return String.join("\n", messages); diff --git a/Common/src/main/java/dev/brighten/antivpn/database/VPNDatabase.java b/Common/src/main/java/dev/brighten/antivpn/database/VPNDatabase.java new file mode 100644 index 0000000..3e64f57 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/database/VPNDatabase.java @@ -0,0 +1,28 @@ +package dev.brighten.antivpn.database; + +import dev.brighten.antivpn.utils.VPNResponse; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Consumer; + +public interface VPNDatabase { + Optional getStoredResponse(String ip); + + void cacheResponse(VPNResponse toCache); + + boolean isWhitelisted(UUID uuid); + + void setWhitelisted(UUID uuid, boolean whitelisted); + + List getAllWhitelisted(); + + void getStoredResponseAsync(String ip, Consumer> result); + + void isWhitelistedAsync(UUID uuid, Consumer result); + + void init(); + + void shutdown(); +} diff --git a/Common/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java b/Common/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java new file mode 100644 index 0000000..d3dd329 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java @@ -0,0 +1,160 @@ +package dev.brighten.antivpn.database.sql; + +import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.api.VPNExecutor; +import dev.brighten.antivpn.database.VPNDatabase; +import dev.brighten.antivpn.database.sql.utils.MySQL; +import dev.brighten.antivpn.database.sql.utils.Query; +import dev.brighten.antivpn.utils.VPNResponse; +import lombok.SneakyThrows; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +public class MySqlVPN implements VPNDatabase { + + private Thread whitelistedThread; + + public MySqlVPN() { + whitelistedThread = new Thread(() -> { + try { + Thread.sleep(TimeUnit.SECONDS.toMillis(8)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + while(true) { + //Updating from database + if(AntiVPN.getInstance().getConfig().isDatabaseEnabled()) { + AntiVPN.getInstance().getExecutor().getWhitelisted().clear(); + AntiVPN.getInstance().getExecutor().getWhitelisted() + .addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted()); + } + try { + Thread.sleep(TimeUnit.SECONDS.toMillis(4)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + + whitelistedThread.start();; + } + @Override + public Optional getStoredResponse(String ip) { + if(!AntiVPN.getInstance().getConfig().isDatabaseEnabled()) return Optional.empty(); + ResultSet rs = Query.prepare("select * from `responses` where `ip` = ? limit 1") + .append(ip).executeQuery(); + + try { + if(rs != null && !rs.wasNull() && rs.next()) { + VPNResponse response = new VPNResponse(rs.getString("asn"), rs.getString("ip"), + rs.getString("countryName"), rs.getString("countryCode"), + rs.getString("city"), rs.getString("timeZone"), + rs.getString("method"), rs.getString("isp"), + rs.getBoolean("proxy"), rs.getBoolean("cached"), true, + rs.getDouble("latitude"), rs.getDouble("longitude"), + System.currentTimeMillis(), -1); + return Optional.of(response); + } + } catch (SQLException throwables) { + throwables.printStackTrace(); + } + + return Optional.empty(); + } + + /*Query.prepare("create table if not exists `responses` (`ip` varchar(45) not null, " + + "`countryName` varchar(64), `countryCode` varchar(10), `city` varchar(64), `timeZone` varchar(64), " + + "`method` varchar(32), `isp` varchar(32), `proxy` boolean, `cached` boolean " + + "`latitude` double, `longitude` double)");*/ + @Override + public void cacheResponse(VPNResponse toCache) { + if(!AntiVPN.getInstance().getConfig().isDatabaseEnabled()) return; + Query.prepare("insert into `responses` (`ip`,`asn`,`countryName`,`countryCode`,`city`,`timeZone`," + + "`method`,`isp`,`proxy`,`cached`,`inserted`,`latitude`,`longitude`) values (?,?,?,?,?,?,?,?,?,?,?,?,?)") + .append(toCache.getIp()).append(toCache.getAsn()).append(toCache.getCountryName()) + .append(toCache.getCountryCode()).append(toCache.getCity()) + .append(toCache.getTimeZone()).append(toCache.getMethod()).append(toCache.getIsp()) + .append(toCache.isProxy()).append(toCache.isCached()).append(new Timestamp(System.currentTimeMillis())) + .append(toCache.getLatitude()).append(toCache.getLongitude()).execute(); + } + + @SneakyThrows + @Override + public boolean isWhitelisted(UUID uuid) { + if(!AntiVPN.getInstance().getConfig().isDatabaseEnabled()) return false; + ResultSet set = Query.prepare("select uuid from `whitelisted` where `uuid` = ? limit 1") + .append(uuid.toString()).executeQuery(); + + + return set != null && !set.wasNull() && set.next() && set.getString("uuid") != null; + } + + @Override + public void setWhitelisted(UUID uuid, boolean whitelisted) { + if(whitelisted) { + if(!isWhitelisted(uuid)) { + Query.prepare("insert into `whitelisted` (`uuid`) values (?)").append(uuid.toString()).execute(); + } + AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid); + } else { + Query.prepare("delete from `whitelisted` where `uuid` = ?").append(uuid.toString()).execute(); + AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid); + } + } + + @Override + public List getAllWhitelisted() { + List uuids = new ArrayList<>(); + ResultSet set = Query.prepare("select uuid from `whitelisted`").executeQuery(); + + try { + while(set.next()) { + uuids.add(UUID.fromString(set.getString("uuid"))); + } + } catch(SQLException e) { + e.printStackTrace(); + } + return uuids; + } + + @Override + public void getStoredResponseAsync(String ip, Consumer> result) { + VPNExecutor.threadExecutor.execute(() -> result.accept(getStoredResponse(ip))); + } + + @Override + public void isWhitelistedAsync(UUID uuid, Consumer result) { + VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(uuid))); + } + + @Override + public void init() { + if(!AntiVPN.getInstance().getConfig().isDatabaseEnabled()) return; + System.out.println("Initializing MySQL..."); + MySQL.init(); + + System.out.println("Creating tables..."); + Query.prepare("create table if not exists `whitelisted` (`uuid` varchar(36) not null)").execute(); + Query.prepare("create table if not exists `responses` (`ip` varchar(45) not null, `asn` varchar(12)," + + "`countryName` varchar(64), `countryCode` varchar(10), `city` varchar(64), `timeZone` varchar(64), " + + "`method` varchar(32), `isp` varchar(32), `proxy` boolean, `cached` boolean, `inserted` timestamp," + + "`latitude` double, `longitude` double)").execute(); + + System.out.println("Creating indexes..."); + Query.prepare("create index if not exists `uuid_1` on `whitelisted` (`uuid`)").execute(); + Query.prepare("create index if not exists `ip_1` on `responses` (`ip`)").execute(); + Query.prepare("create index if not exists `proxy_1` on `responses` (`proxy`)").execute(); + Query.prepare("create index if not exists `inserted_1` on `responses` (`inserted`)").execute(); + } + + @Override + public void shutdown() { + if(!AntiVPN.getInstance().getConfig().isDatabaseEnabled()) return; + MySQL.shutdown(); + } +} diff --git a/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/ExecutableStatement.java b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/ExecutableStatement.java new file mode 100644 index 0000000..f226fd0 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/ExecutableStatement.java @@ -0,0 +1,138 @@ +package dev.brighten.antivpn.database.sql.utils; + +import dev.brighten.antivpn.utils.MiscUtils; +import lombok.SneakyThrows; + +import java.sql.*; +import java.util.UUID; + +public class ExecutableStatement { + private PreparedStatement statement; + private int pos = 1; + + public ExecutableStatement(PreparedStatement statement) { + this.statement = statement; + } + + @SneakyThrows + public Integer execute() { + try { + return statement.executeUpdate(); + } finally { + MiscUtils.close(statement); + } + } + + @SneakyThrows + public void execute(ResultSetIterator iterator) { + ResultSet rs = null; + try { + rs = statement.executeQuery(); + while (rs.next()) iterator.next(rs); + } finally { + MiscUtils.close(statement, rs); + } + } + + @SneakyThrows + public void executeSingle(ResultSetIterator iterator) { + ResultSet rs = null; + try { + rs = statement.executeQuery(); + if (rs.next()) iterator.next(rs); + else iterator.next(null); + } finally { + MiscUtils.close(statement, rs); + } + } + + @SneakyThrows + public ResultSet executeQuery() { + return statement.executeQuery(); + } + + @SneakyThrows + public ExecutableStatement append(Object obj) { + statement.setObject(pos++, obj); + return this; + } + + @SneakyThrows + public ExecutableStatement append(String obj) { + statement.setString(pos++, obj); + return this; + } + + @SneakyThrows + public ExecutableStatement append(UUID uuid) { + if (uuid != null) statement.setString(pos++, uuid.toString().replace("-", "")); + else statement.setString(pos++, null); + return this; + } + + @SneakyThrows + public ExecutableStatement append(Array obj) { + statement.setArray(pos++, obj); + return this; + } + + @SneakyThrows + public ExecutableStatement append(Integer obj) { + statement.setInt(pos++, obj); + return this; + } + + @SneakyThrows + public ExecutableStatement append(Short obj) { + statement.setShort(pos++, obj); + return this; + } + + @SneakyThrows + public ExecutableStatement append(Long obj) { + statement.setLong(pos++, obj); + return this; + } + + @SneakyThrows + public ExecutableStatement append(Float obj) { + statement.setFloat(pos++, obj); + return this; + } + + @SneakyThrows + public ExecutableStatement append(Double obj) { + statement.setDouble(pos++, obj); + return this; + } + + @SneakyThrows + public ExecutableStatement append(Date obj) { + statement.setDate(pos++, obj); + return this; + } + + @SneakyThrows + public ExecutableStatement append(Timestamp obj) { + statement.setTimestamp(pos++, obj); + return this; + } + + @SneakyThrows + public ExecutableStatement append(Time obj) { + statement.setTime(pos++, obj); + return this; + } + + @SneakyThrows + public ExecutableStatement append(Blob obj) { + statement.setBlob(pos++, obj); + return this; + } + + @SneakyThrows + public ExecutableStatement append(byte[] obj) { + statement.setBytes(pos++, obj); + return this; + } +} diff --git a/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java new file mode 100644 index 0000000..90febde --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java @@ -0,0 +1,51 @@ +package dev.brighten.antivpn.database.sql.utils; + +import dev.brighten.antivpn.AntiVPN; + +import java.sql.Connection; +import java.sql.DriverManager; + +public class MySQL { + private static Connection conn; + + public static void init() { + try { + if (conn == null || conn.isClosed()) { + Class.forName("com.mysql.jdbc.Driver"); + conn = DriverManager.getConnection("jdbc:mysql://" + AntiVPN.getInstance().getConfig().getIp() + + ":" + AntiVPN.getInstance().getConfig().getPort() + + "/?useSSL=true&autoReconnect=true", + AntiVPN.getInstance().getConfig().getUsername(), + AntiVPN.getInstance().getConfig().getPassword()); + conn.setAutoCommit(true); + Query.use(conn); + Query.prepare("CREATE DATABASE IF NOT EXISTS `" + + AntiVPN.getInstance().getConfig().getDatabaseName() + "`").execute(); + Query.prepare("USE `" + AntiVPN.getInstance().getConfig().getDatabaseName() + "`").execute(); + System.out.println("Connection to MySQL has been established."); + } + } catch (Exception e) { + System.out.println("Failed to load mysql: " + e.getMessage()); + e.printStackTrace(); + } + } + + public static void use() { + try { + init(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void shutdown() { + try { + if(conn != null && !conn.isClosed()) { + conn.close(); + conn = null; + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/Query.java b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/Query.java new file mode 100644 index 0000000..f2f86fe --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/Query.java @@ -0,0 +1,25 @@ +package dev.brighten.antivpn.database.sql.utils; + +import lombok.SneakyThrows; + +import java.sql.Connection; + +public class Query { + private static Connection conn; + + public static void use(Connection conn) { + Query.conn = conn; + } + + @SneakyThrows + public static ExecutableStatement prepare(String query) { + return new ExecutableStatement(conn.prepareStatement(query)); + } + + + + @SneakyThrows + public static ExecutableStatement prepare(String query, Connection con) { + return new ExecutableStatement(con.prepareStatement(query)); + } +} diff --git a/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/ResultSetIterator.java b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/ResultSetIterator.java new file mode 100644 index 0000000..e99cd7c --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/ResultSetIterator.java @@ -0,0 +1,7 @@ +package dev.brighten.antivpn.database.sql.utils; + +import java.sql.ResultSet; + +public interface ResultSetIterator { + void next(ResultSet rs) throws Exception; +} diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java b/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java new file mode 100644 index 0000000..fc93710 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java @@ -0,0 +1,23 @@ +package dev.brighten.antivpn.utils; + +import java.io.Closeable; + +public class MiscUtils { + + public static void close(Closeable... closeables) { + try { + for (Closeable closeable : closeables) if (closeable != null) closeable.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void close(AutoCloseable... closeables) { + try { + for (AutoCloseable closeable : closeables) if (closeable != null) closeable.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + +}