From 9f7f4b40f0e82e6218aa35403c766008c72425fb Mon Sep 17 00:00:00 2001 From: Dawson Date: Mon, 5 Jan 2026 09:21:32 -0500 Subject: [PATCH] Implementing database versioning for CIDR whitelisting --- .../antivpn/bungee/BungeeListener.java | 10 -- .../java/dev/brighten/antivpn/AntiVPN.java | 2 +- .../dev/brighten/antivpn/api/APIPlayer.java | 20 ++- .../dev/brighten/antivpn/api/VPNConfig.java | 159 ++++++------------ .../dev/brighten/antivpn/api/VPNExecutor.java | 9 +- .../command/impl/ClearCacheCommand.java | 19 ++- .../antivpn/database/local/H2VPN.java | 109 +++++++++--- .../antivpn/database/local/version/First.java | 44 +++-- .../database/local/version/Second.java | 122 ++++++++++++++ .../antivpn/database/mongo/MongoVPN.java | 24 +-- .../antivpn/database/sql/MySqlVPN.java | 16 +- .../sql/utils/ExecutableStatement.java | 43 ++--- .../antivpn/database/sql/utils/MySQL.java | 4 +- .../antivpn/database/sql/utils/Query.java | 3 + .../database/sql/utils/ResultSetIterator.java | 3 +- .../antivpn/database/version/H2Version.java | 38 ++++- .../antivpn/database/version/Version.java | 20 ++- .../dev/brighten/antivpn/utils/IpUtils.java | 109 ++++++++++++ .../antivpn/velocity/VelocityListener.java | 10 +- 19 files changed, 551 insertions(+), 213 deletions(-) create mode 100644 Common/Source/src/main/java/dev/brighten/antivpn/database/local/version/Second.java create mode 100644 Common/Source/src/main/java/dev/brighten/antivpn/utils/IpUtils.java diff --git a/Bungee/BungeePlugin/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java b/Bungee/BungeePlugin/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java index 116322c..7a11eea 100644 --- a/Bungee/BungeePlugin/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java +++ b/Bungee/BungeePlugin/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java @@ -28,16 +28,6 @@ public class BungeeListener extends VPNExecutor implements Listener { .registerListener(BungeePlugin.pluginInstance.getPlugin(), this); } - @Override - public void shutdown() { - if(cacheResetTask != null) { - cacheResetTask.cancel(); - cacheResetTask = null; - } - BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListener(this); - super.shutdown(); - } - @Override public void log(Level level, String log, Object... objects) { BungeePlugin.pluginInstance.getProxy().getLogger().log(Level.INFO, String.format(log, objects)); diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/AntiVPN.java b/Common/Source/src/main/java/dev/brighten/antivpn/AntiVPN.java index 6fe3cdb..c1f2c16 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/AntiVPN.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/AntiVPN.java @@ -190,7 +190,7 @@ public class AntiVPN { executor.log("Failed to deregister H2 driver: " + e.getMessage()); } } - VPNExecutor.threadExecutor.shutdown(); + AntiVPN.getInstance().getExecutor().getThreadExecutor().shutdown(); if(database != null) database.shutdown(); } diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/api/APIPlayer.java b/Common/Source/src/main/java/dev/brighten/antivpn/api/APIPlayer.java index 6397f05..3f4a35a 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/api/APIPlayer.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/api/APIPlayer.java @@ -1,3 +1,19 @@ +/* + * Copyright 2026 Dawson Hessler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package dev.brighten.antivpn.api; import com.github.benmanes.caffeine.cache.Cache; @@ -39,7 +55,7 @@ public abstract class APIPlayer { public void updateAlertsState() { //Updating into database so its synced across servers and saved on logout. - AntiVPN.getInstance().getDatabase().updateAlertsState(uuid, alertsEnabled); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> AntiVPN.getInstance().getDatabase().updateAlertsState(uuid, alertsEnabled)); } public CheckResult checkPlayer(Consumer onKick) { @@ -81,7 +97,7 @@ public abstract class APIPlayer { // the state, it will kick. && AntiVPN.getInstance().getVpnConfig().getCountryList() .contains(result.getCountryCode()) - != AntiVPN.getInstance().getVpnConfig().isWhitelistCountries()) { + != AntiVPN.getInstance().getVpnConfig().getWhitelistCountries()) { //Using our built in kicking system if no commands are configured checkResult = new CheckResult(result, ResultType.DENIED_COUNTRY); } else if (result.isProxy()) { diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/api/VPNConfig.java b/Common/Source/src/main/java/dev/brighten/antivpn/api/VPNConfig.java index 18ebd6d..c8c584b 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/api/VPNConfig.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/api/VPNConfig.java @@ -1,7 +1,24 @@ +/* + * Copyright 2026 Dawson Hessler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package dev.brighten.antivpn.api; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.utils.ConfigDefault; +import lombok.Getter; import java.util.ArrayList; import java.util.Collections; @@ -52,37 +69,50 @@ public class VPNConfig { defCountrylist = new ConfigDefault<>(new ArrayList<>(), "countries.list", AntiVPN.getInstance()); - private String license, kickMessage, databaseType, databaseName, mongoURL, username, password, ip, alertMsg, - countryVanillaKickReason; - private List prefixWhitelists, commands, countryList, countryKickCommands; + @Getter + private String license; + @Getter + private String kickMessage; + @Getter + private String databaseType; + @Getter + private String databaseName; + private String mongoURL; + @Getter + private String username; + @Getter + private String password; + @Getter + private String ip; + private String alertMsg; + @Getter + private String countryVanillaKickReason; + @Getter + private List prefixWhitelists; + private List commands; + @Getter + private List countryList; + private List countryKickCommands; private int port; - private boolean cacheResults, databaseEnabled, useCredentials, commandsEnabled, kickPlayers, alertToStaff, - metrics, whitelistCountries; + private boolean cacheResults; + @Getter + private boolean databaseEnabled; + private boolean useCredentials; + private boolean commandsEnabled; + @Getter + private boolean kickPlayers; + private boolean alertToStaff; + private boolean metrics; + private boolean whitelistCountries; /** - * License from https://funkemunky.cc/shop to be used for more queries. - * @return String - */ - public String getLicense() { - return license; - } - - /** - * If true, results will be cached to reduce queries to https://funkemunky.cc + * If true, results will be cached to reduce queries to ... * @return boolean */ public boolean cachedResults() { return cacheResults; } - /** - * Will be used for vanilla kick message when {@link VPNConfig#runCommands()} is true. - * @return String - */ - public String getKickString() { - return kickMessage; - } - /** * Message to send staff on proxy detection. * @return String @@ -115,31 +145,6 @@ public class VPNConfig { return commands; } - /** - * If false, no commands nor kick will be run on proxy detection. - * @return boolean - */ - public boolean kickPlayersOnDetect() { - return kickPlayers; - } - - /** - * Returns Strings of which are checked against the beginning of player names. Used to - * allow Geyser-connected players to join. - * @return List - */ - public List getPrefixWhitelists() { - return prefixWhitelists; - } - - /** - * Returns true if we want to use a database - * @return boolean - */ - public boolean isDatabaseEnabled() { - return databaseEnabled; - } - /** * Whether or not the database we want to connect to requires credentials. * @return boolean @@ -156,59 +161,11 @@ public class VPNConfig { return mongoURL; } - /** - * Database type. Either MySQL and Mongo. - * @return String - */ - public String getDatabaseType() { - return databaseType; - } - - /** - * Database name - * @return String - */ - public String getDatabaseName() { - return databaseName; - } - - /** - * Database username - * @return String - */ - public String getUsername() { - return username; - } - - /** - * Database Password - * @return String - */ - public String getPassword() { - return password; - } - - /** - * Database IP - * @return String - */ - public String getIp() { - return ip; - } - - /** - * Returns the list of ISO country codes we need to check. - * @return List - */ - public List countryList() { - return countryList; - } - /** * If true, we only allow the {@link VPNConfig#countryKickCommands()}. If false, we blacklist them. * @return boolean */ - public boolean whitelistCountries() { + public boolean getWhitelistCountries() { return whitelistCountries; } @@ -220,14 +177,6 @@ public class VPNConfig { return countryKickCommands; } - /** - * Returns the vanilla kick reason for bad country locations - * @return String - */ - public String countryVanillaKickReason() { - return countryVanillaKickReason; - } - /** * Gets the port based on configuration. If {@link VPNConfig#port} is -1, will get default port * based on {@link VPNConfig#getDatabaseType()} lowerCase(). @@ -251,7 +200,7 @@ public class VPNConfig { /** - * If true, https://bstats.org metrics will be collected to improve KauriVPN. + * If true, ... metrics will be collected to improve KauriVPN. * @return boolean */ public boolean metrics() { diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java b/Common/Source/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java index 6635f94..a2f8fe0 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java @@ -17,7 +17,8 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; public abstract class VPNExecutor { - public static ScheduledExecutorService threadExecutor = Executors.newScheduledThreadPool(2); + @Getter + private ScheduledExecutorService threadExecutor = Executors.newScheduledThreadPool(2); @Getter private final Set whitelisted = Collections.synchronizedSet(new HashSet<>()); @@ -75,12 +76,12 @@ public abstract class VPNExecutor { StringUtil.varReplace(dev.brighten.antivpn.AntiVPN.getInstance().getVpnConfig() .alertMessage(), player, result.response())))); - if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { + if(AntiVPN.getInstance().getVpnConfig().isKickPlayers()) { switch (result.resultType()) { case DENIED_PROXY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig() - .getKickString(), player, result.response())); + .getKickMessage(), player, result.response())); case DENIED_COUNTRY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig() - .countryVanillaKickReason(), player, result.response())); + .getCountryVanillaKickReason(), player, result.response())); } } diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/command/impl/ClearCacheCommand.java b/Common/Source/src/main/java/dev/brighten/antivpn/command/impl/ClearCacheCommand.java index 3ff5971..01ec9a5 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/command/impl/ClearCacheCommand.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/command/impl/ClearCacheCommand.java @@ -1,6 +1,23 @@ +/* + * Copyright 2026 Dawson Hessler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package dev.brighten.antivpn.command.impl; import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.api.VPNExecutor; import dev.brighten.antivpn.command.Command; import dev.brighten.antivpn.command.CommandExecutor; @@ -45,7 +62,7 @@ public class ClearCacheCommand extends Command { @Override public String execute(CommandExecutor executor, String[] args) { - AntiVPN.getInstance().getDatabase().clearResponses(); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> AntiVPN.getInstance().getDatabase().clearResponses()); return "&aCleared all cached API response information!"; } diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/database/local/H2VPN.java b/Common/Source/src/main/java/dev/brighten/antivpn/database/local/H2VPN.java index 59a75ea..6498a22 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/database/local/H2VPN.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/database/local/H2VPN.java @@ -1,3 +1,19 @@ +/* + * Copyright 2026 Dawson Hessler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package dev.brighten.antivpn.database.local; import com.github.benmanes.caffeine.cache.Cache; @@ -7,6 +23,7 @@ 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.database.version.H2Version; import dev.brighten.antivpn.web.objects.VPNResponse; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -30,7 +47,7 @@ public class H2VPN implements VPNDatabase { public H2VPN() { - VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> { + AntiVPN.getInstance().getExecutor().getThreadExecutor().scheduleAtFixedRate(() -> { if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return; //Refreshing whitelisted players @@ -143,14 +160,18 @@ public class H2VPN implements VPNDatabase { if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return; - if (whitelisted) { - if (!isWhitelisted(uuid)) { - Query.prepare("insert into `whitelisted` (`uuid`) values (?)").append(uuid.toString()).execute(); + try { + 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); } - AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid); - } else { - Query.prepare("delete from `whitelisted` where `uuid` = ?").append(uuid.toString()).execute(); - AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid); + } catch (SQLException e) { + AntiVPN.getInstance().getExecutor().logException("Could not set whitelist for uuid '" + uuid + "' due to SQL error.", e); } } @@ -159,14 +180,18 @@ public class H2VPN implements VPNDatabase { if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return; - if(whitelisted) { - if(!isWhitelisted(ip)) { - Query.prepare("insert into `whitelisted-ips` (`ip`) values (?)").append(ip).execute(); + try { + if(whitelisted) { + if(!isWhitelisted(ip)) { + Query.prepare("insert into `whitelisted-ips` (`ip`) values (?)").append(ip).execute(); + } + AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(ip); + } else { + Query.prepare("delete from `whitelisted-ips` where `ip` = ?").append(ip).execute(); + AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(ip); } - AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(ip); - } else { - Query.prepare("delete from `whitelisted-ips` where `ip` = ?").append(ip).execute(); - AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(ip); + } catch (SQLException e) { + AntiVPN.getInstance().getExecutor().logException("Could not set whitelist for ip '" + ip + "' due to SQL error.", e); } } @@ -174,8 +199,12 @@ public class H2VPN implements VPNDatabase { public List getAllWhitelisted() { List uuids = new ArrayList<>(); - if(!MySQL.isClosed()) Query.prepare("select uuid from `whitelisted`") - .execute(set -> uuids.add(UUID.fromString(set.getString("uuid")))); + try { + if(!MySQL.isClosed()) Query.prepare("select uuid from `whitelisted`") + .execute(set -> uuids.add(UUID.fromString(set.getString("uuid")))); + } catch (SQLException e) { + AntiVPN.getInstance().getExecutor().logException("Could not get all whitelisted players due to SQL error.", e); + } return uuids; } @@ -184,8 +213,12 @@ public class H2VPN implements VPNDatabase { public List getAllWhitelistedIps() { List ips = new ArrayList<>(); - if(!MySQL.isClosed()) Query.prepare("select `ip` from `whitelisted-ips`") + try { + if(!MySQL.isClosed()) Query.prepare("select `ip` from `whitelisted-ips`") .execute(set -> ips.add(set.getString("ip"))); + } catch (SQLException e) { + AntiVPN.getInstance().getExecutor().logException("Could not get all whitelisted ips due to SQL error.", e); + } return ips; } @@ -194,28 +227,28 @@ public class H2VPN implements VPNDatabase { public void getStoredResponseAsync(String ip, Consumer> result) { if(MySQL.isClosed()) return; - VPNExecutor.threadExecutor.execute(() -> result.accept(getStoredResponse(ip))); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> result.accept(getStoredResponse(ip))); } @Override public void isWhitelistedAsync(UUID uuid, Consumer result) { if(MySQL.isClosed()) return; - VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(uuid))); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> result.accept(isWhitelisted(uuid))); } @Override public void isWhitelistedAsync(String ip, Consumer result) { if(MySQL.isClosed()) return; - VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(ip))); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> result.accept(isWhitelisted(ip))); } @Override public void alertsState(UUID uuid, Consumer result) { if(MySQL.isClosed()) return; - VPNExecutor.threadExecutor.execute(() -> { + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> { try(ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1") .append(uuid.toString()).executeQuery()) { @@ -235,22 +268,35 @@ public class H2VPN implements VPNDatabase { //We want to make sure there isn't already a uuid inserted to prevent double insertions alertsState(uuid, alreadyEnabled -> { //No need to make another thread execute, already async if(!alreadyEnabled) { - Query.prepare("insert into `alerts` (`uuid`) values (?)").append(uuid.toString()) - .execute(); + try { + Query.prepare("insert into `alerts` (`uuid`) values (?)").append(uuid.toString()) + .execute(); + } catch (SQLException e) { + AntiVPN.getInstance().getExecutor().logException("There was a problem updating alerts state for " + uuid, e); + } } //No need to insert again of already enabled }); //Removing any uuid from the alerts table will disable alerts globally. - } else VPNExecutor.threadExecutor.execute(() -> + } else { + try { Query.prepare("delete from `alerts` where `uuid` = ?") .append(uuid.toString()) - .execute()); + .execute(); + } catch (SQLException e) { + AntiVPN.getInstance().getExecutor().logException("There was a problem updating alerts state for " + uuid, e); + } + } } @Override public void clearResponses() { if(MySQL.isClosed()) return; - VPNExecutor.threadExecutor.execute(() -> Query.prepare("delete from `responses`").execute()); + try { + Query.prepare("delete from `responses`").execute(); + } catch (SQLException e) { + AntiVPN.getInstance().getExecutor().logException("There was a problem clearing responses.", e); + } } @Override @@ -259,6 +305,15 @@ public class H2VPN implements VPNDatabase { return; AntiVPN.getInstance().getExecutor().log("Initializing H2..."); MySQL.initH2(); + try { + for (H2Version version : H2Version.versions) { + if(version.needsUpdate(this)) { + version.update(this); + } + } + } catch (Exception e) { + throw new RuntimeException("Could not complete version setup due to SQL error", e); + } AntiVPN.getInstance().getExecutor().log("Creating tables..."); diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/database/local/version/First.java b/Common/Source/src/main/java/dev/brighten/antivpn/database/local/version/First.java index de04173..e2fa481 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/database/local/version/First.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/database/local/version/First.java @@ -1,3 +1,19 @@ +/* + * Copyright 2026 Dawson Hessler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package dev.brighten.antivpn.database.local.version; import dev.brighten.antivpn.AntiVPN; @@ -5,9 +21,13 @@ import dev.brighten.antivpn.database.local.H2VPN; import dev.brighten.antivpn.database.sql.utils.Query; import dev.brighten.antivpn.database.version.H2Version; +import java.sql.ResultSet; +import java.sql.SQLException; + public class First implements H2Version { @Override - public void update(H2VPN database) throws Exception { + public void update(H2VPN database) throws SQLException { + backupDatabase(); Query.prepare("create table if not exists `whitelisted` (`uuid` varchar(36) not null)").execute(); Query.prepare("create table if not exists `whitelisted-ips` (`ip` varchar(45) not null)").execute(); Query.prepare("create table if not exists `responses` (`ip` varchar(45) not null, `asn` varchar(12)," @@ -15,17 +35,15 @@ public class First implements H2Version { + "`method` varchar(32), `isp` text, `proxy` boolean, `cached` boolean, `inserted` timestamp," + "`latitude` double, `longitude` double)").execute(); Query.prepare("create table if not exists `alerts` (`uuid` varchar(36) not null)").execute(); + Query.prepare("create table if not exists `database_version` (`version` int)").execute(); + Query.prepare("insert into `database_version` (`version`) values (?)").append(versionNumber()).execute(); AntiVPN.getInstance().getExecutor().log("Creating indexes..."); - try { - 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(); - Query.prepare("create index if not exists `ip_1` on `whitelisted-ips` (`ip`)").execute(); - } catch (Exception e) { - System.err.println("MySQL Excepton created" + e.getMessage()); - } + 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(); + Query.prepare("create index if not exists `ip_1` on `whitelisted-ips` (`ip`)").execute(); } @Override @@ -35,6 +53,10 @@ public class First implements H2Version { @Override public boolean needsUpdate(H2VPN database) { - return false; + try(ResultSet set = Query.prepare("select * from `database_version` where version = 0").executeQuery()) { + return set.getFetchSize() == 0; + } catch (SQLException e) { + return true; + } } } diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/database/local/version/Second.java b/Common/Source/src/main/java/dev/brighten/antivpn/database/local/version/Second.java new file mode 100644 index 0000000..348ee39 --- /dev/null +++ b/Common/Source/src/main/java/dev/brighten/antivpn/database/local/version/Second.java @@ -0,0 +1,122 @@ +/* + * Copyright 2026 Dawson Hessler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.brighten.antivpn.database.local.version; + +import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.database.local.H2VPN; +import dev.brighten.antivpn.database.sql.utils.Query; +import dev.brighten.antivpn.database.version.H2Version; +import dev.brighten.antivpn.utils.CIDRUtils; +import dev.brighten.antivpn.utils.IpUtils; + +import java.net.UnknownHostException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class Second implements H2Version { + @Override + public void update(H2VPN database) throws SQLException { + backupDatabase(); + List whitelistedIps = new ArrayList<>(); + + try (var set = Query.prepare("SELECT * FROM `whitelisted-ips`").executeQuery()) { + while (set.next()) { + whitelistedIps.add(set.getString("ip")); + } + } + + try { + Query.prepare("CREATE TABLE IF NOT EXISTS `whitelisted-ranges` " + + "(id INT AUTO_INCREMENT PRIMARY KEY, " + + "cidr_string VARCHAR(45), " + + "ip_start BIGINT NOT NULL, " + + "ip_end BIGINT NOT NULL") + .execute(); + Query.prepare("CREATE INDEX idx_ip_range ON `whitelisted-ranges` (ip_start, ip_end)").execute(); + + var cidrs = whitelistedIps.stream().map(ip -> { + try { + return new CIDRUtils(ip + "/32"); + } catch (UnknownHostException e) { + throw new RuntimeException("Could not format ip " + ip + " into a CIDR!", e); + } + }).toList(); + var insertStatement = Query.prepare("INSERT INTO `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) VALUES (?, ?, ?)"); + for (CIDRUtils cidr : cidrs) { + insertStatement = insertStatement + .append(cidr.toString()). + append(IpUtils.getIpDecimal(cidr.getStartAddress().getHostAddress()).orElseThrow()) + .append(IpUtils.getIpDecimal(cidr.getEndAddress().getHostAddress()).orElseThrow()) + .addBatch(); + } + + int[] updateCounts = insertStatement.executeBatch(); + + for (int updateCount : updateCounts) { + if(updateCount == 0) { + throw new RuntimeException("Could not insert a CIDR from previous whitelisted lists, attempted to restore previous database!"); + } + } + + Query.prepare("DROP INDEX ip_1 on `whitelisted-ips`").execute(); + Query.prepare("DROP TABLE `whitelisted-ips`").execute(); + Query.prepare("INSERT INTO `database_version` (`version`) VALUES (?)").append(versionNumber()).execute(); + } catch (Throwable e) { + AntiVPN.getInstance().getExecutor().log("Failed to update database to version 1: " + e.getMessage()); + rollback(whitelistedIps); + throw e; + } + + } + + private void rollback(List ipAddresses) throws SQLException { + AntiVPN.getInstance().getExecutor().log("Rolling back to version 0..."); + Query.prepare("DROP INDEX idx_ip_range ON `whitelisted-ranges`").execute(); + Query.prepare("DROP TABLE `whitelisted-ranges`").execute(); + Query.prepare("DELETE FROM `database_version` WHERE version = ?").append(versionNumber()).execute(); + + Query.prepare("CREATE TABLE IF NOT EXISTS `whitelisted-ips` (`ip` VARCHAR(45) NOT NULL)") + .execute(); + Query.prepare("create index if not exists `ip_1` on `whitelisted-ips` (`ip`)").execute(); + + Query.prepare("DELETE FROM `whitelisted-ips`").execute(); + + var statement = Query.prepare("INSERT INTO `whitelisted-ips` (`ip`) VALUES (?)"); + for (String ip : ipAddresses) { + statement.append(ip); + statement.addBatch(); + } + + statement.executeBatch(); + } + + @Override + public int versionNumber() { + return 1; + } + + @Override + public boolean needsUpdate(H2VPN database) { + try (ResultSet set = Query.prepare("select * from `database_version` where version = 1").executeQuery()) { + return set.getFetchSize() == 0; + } catch (SQLException e) { + return true; + } + } +} diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/database/mongo/MongoVPN.java b/Common/Source/src/main/java/dev/brighten/antivpn/database/mongo/MongoVPN.java index 2e7c4eb..c317fd8 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/database/mongo/MongoVPN.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/database/mongo/MongoVPN.java @@ -32,7 +32,7 @@ public class MongoVPN implements VPNDatabase { .build(); public MongoVPN() { - VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> { + AntiVPN.getInstance().getExecutor().getThreadExecutor().scheduleAtFixedRate(() -> { if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return; //Refreshing whitelisted players @@ -55,7 +55,7 @@ public class MongoVPN implements VPNDatabase { long lastUpdate = rdoc.get("lastAccess", 0L); if(System.currentTimeMillis() - lastUpdate > TimeUnit.HOURS.toMillis(1)) { - VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip)); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> deleteResponse(ip)); return null; } @@ -101,7 +101,7 @@ public class MongoVPN implements VPNDatabase { cachedResponses.put(toCache.getIp(), toCache); - VPNExecutor.threadExecutor.execute(() -> { + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> { Bson update = new Document("$set", rdoc); cacheDocument.updateOne(Filters.eq("ip", toCache.getIp()), update, new UpdateOptions().upsert(true)); @@ -133,10 +133,10 @@ public class MongoVPN implements VPNDatabase { Document wdoc = new Document("setting", "whitelist"); wdoc.put("uuid", uuid.toString()); AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid); - VPNExecutor.threadExecutor.execute(() -> settingsDocument.insertOne(wdoc)); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> settingsDocument.insertOne(wdoc)); } else { AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid); - VPNExecutor.threadExecutor.execute(() -> settingsDocument.deleteMany(Filters + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> settingsDocument.deleteMany(Filters .and( Filters.eq("setting", "whitelist"), Filters.eq("uuid", uuid.toString())))); @@ -149,10 +149,10 @@ public class MongoVPN implements VPNDatabase { Document wdoc = new Document("setting", "whitelist").append("ip", ip); AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(ip); - VPNExecutor.threadExecutor.execute(() -> settingsDocument.insertOne(wdoc)); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> settingsDocument.insertOne(wdoc)); } else { AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(ip); - VPNExecutor.threadExecutor.execute(() -> settingsDocument.deleteMany(Filters + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> settingsDocument.deleteMany(Filters .and( Filters.eq("setting", "whitelist"), Filters.eq("ip", ip)))); @@ -179,29 +179,29 @@ public class MongoVPN implements VPNDatabase { @Override public void getStoredResponseAsync(String ip, Consumer> result) { - VPNExecutor.threadExecutor.execute(() -> result.accept(getStoredResponse(ip))); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> result.accept(getStoredResponse(ip))); } @Override public void isWhitelistedAsync(UUID uuid, Consumer result) { - VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(uuid))); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> result.accept(isWhitelisted(uuid))); } @Override public void isWhitelistedAsync(String ip, Consumer result) { - VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(ip))); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> result.accept(isWhitelisted(ip))); } @Override public void alertsState(UUID uuid, Consumer result) { - VPNExecutor.threadExecutor.execute(() -> result.accept(settingsDocument + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> result.accept(settingsDocument .find(Filters.and(Filters.eq("setting", "alerts"), Filters.eq("uuid", uuid.toString()))).first() != null)); } @Override public void updateAlertsState(UUID uuid, boolean state) { - VPNExecutor.threadExecutor.execute(() -> { + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> { settingsDocument.deleteMany(Filters.and(Filters.eq("setting", "alerts"), Filters.eq("uuid", uuid.toString()))); if(state) { diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java b/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java index 148f0c8..e51654f 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java @@ -29,7 +29,7 @@ public class MySqlVPN implements VPNDatabase { public MySqlVPN() { - VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> { + AntiVPN.getInstance().getExecutor().getThreadExecutor().scheduleAtFixedRate(() -> { if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return; //Refreshing whitelisted players @@ -63,7 +63,7 @@ public class MySqlVPN implements VPNDatabase { rs.getTimestamp("inserted").getTime(), -1); if(System.currentTimeMillis() - responseFromDoc.getLastAccess() > TimeUnit.HOURS.toMillis(1)) { - VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip)); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> deleteResponse(ip)); return null; } @@ -190,28 +190,28 @@ public class MySqlVPN implements VPNDatabase { public void getStoredResponseAsync(String ip, Consumer> result) { if(MySQL.isClosed()) return; - VPNExecutor.threadExecutor.execute(() -> result.accept(getStoredResponse(ip))); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> result.accept(getStoredResponse(ip))); } @Override public void isWhitelistedAsync(UUID uuid, Consumer result) { if(MySQL.isClosed()) return; - VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(uuid))); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> result.accept(isWhitelisted(uuid))); } @Override public void isWhitelistedAsync(String ip, Consumer result) { if(MySQL.isClosed()) return; - VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(ip))); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> result.accept(isWhitelisted(ip))); } @Override public void alertsState(UUID uuid, Consumer result) { if(MySQL.isClosed()) return; - VPNExecutor.threadExecutor.execute(() -> { + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> { try(ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1") .append(uuid.toString()).executeQuery()) { @@ -237,7 +237,7 @@ public class MySqlVPN implements VPNDatabase { } //No need to insert again of already enabled }); //Removing any uuid from the alerts table will disable alerts globally. - } else VPNExecutor.threadExecutor.execute(() -> + } else AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> Query.prepare("delete from `alerts` where `uuid` = ?") .append(uuid.toString()) .execute()); @@ -247,7 +247,7 @@ public class MySqlVPN implements VPNDatabase { public void clearResponses() { if(MySQL.isClosed()) return; - VPNExecutor.threadExecutor.execute(() -> Query.prepare("delete from `responses`").execute()); + AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> Query.prepare("delete from `responses`").execute()); } @Override diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/ExecutableStatement.java b/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/ExecutableStatement.java index f226fd0..00c8df5 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/ExecutableStatement.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/ExecutableStatement.java @@ -7,15 +7,14 @@ import java.sql.*; import java.util.UUID; public class ExecutableStatement { - private PreparedStatement statement; + private final PreparedStatement statement; private int pos = 1; public ExecutableStatement(PreparedStatement statement) { this.statement = statement; } - @SneakyThrows - public Integer execute() { + public int execute() throws SQLException { try { return statement.executeUpdate(); } finally { @@ -23,34 +22,30 @@ public class ExecutableStatement { } } - @SneakyThrows - public void execute(ResultSetIterator iterator) { - ResultSet rs = null; - try { - rs = statement.executeQuery(); + public void execute(ResultSetIterator iterator) throws SQLException { + try(var rs = statement.executeQuery()) { while (rs.next()) iterator.next(rs); } finally { - MiscUtils.close(statement, rs); + MiscUtils.close(statement); } } - @SneakyThrows - public void executeSingle(ResultSetIterator iterator) { - ResultSet rs = null; + public int[] executeBatch() throws SQLException { + try { + return statement.executeBatch(); + } finally { + MiscUtils.close(statement); + } + } + + public ResultSet executeQuery() throws SQLException { try { - rs = statement.executeQuery(); - if (rs.next()) iterator.next(rs); - else iterator.next(null); + return statement.executeQuery(); } finally { - MiscUtils.close(statement, rs); + MiscUtils.close(statement); } } - @SneakyThrows - public ResultSet executeQuery() { - return statement.executeQuery(); - } - @SneakyThrows public ExecutableStatement append(Object obj) { statement.setObject(pos++, obj); @@ -135,4 +130,10 @@ public class ExecutableStatement { statement.setBytes(pos++, obj); return this; } + + @SneakyThrows + public ExecutableStatement addBatch() { + statement.addBatch(); + return this; + } } diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java b/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java index e8a1530..f1e366a 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java @@ -81,7 +81,7 @@ public class MySQL { } } - private static void backupOldDB(File dbFile, File dataFolder) { + public static void backupOldDB(File dbFile, File dataFolder) { if (dbFile.exists()) { try { // Optional: Make backup first @@ -91,7 +91,7 @@ public class MySQL { } else { AntiVPN.getInstance().getExecutor().log("Backup directory already exists"); } - File backupFile = new File(backupDir, "database.mv.db.backup_" + System.currentTimeMillis()); + File backupFile = new File(backupDir, dbFile.getName() + ".backup_" + System.currentTimeMillis()); Files.copy(dbFile.toPath(), backupFile.toPath()); // Actually delete the file diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/Query.java b/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/Query.java index 35cc5cf..7806524 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/Query.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/Query.java @@ -14,7 +14,10 @@ public class Query { Query.conn = conn; } + @SuppressWarnings("SqlSourceToSinkFlow") public static ExecutableStatement prepare(@Language("SQL") String sql) throws SQLException { return new ExecutableStatement(conn.prepareStatement(sql)); } + + } diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/ResultSetIterator.java b/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/ResultSetIterator.java index e99cd7c..4e2f911 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/ResultSetIterator.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/database/sql/utils/ResultSetIterator.java @@ -1,7 +1,8 @@ package dev.brighten.antivpn.database.sql.utils; import java.sql.ResultSet; +import java.sql.SQLException; public interface ResultSetIterator { - void next(ResultSet rs) throws Exception; + void next(ResultSet rs) throws SQLException; } diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/database/version/H2Version.java b/Common/Source/src/main/java/dev/brighten/antivpn/database/version/H2Version.java index 653b9a1..e13ce3c 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/database/version/H2Version.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/database/version/H2Version.java @@ -1,15 +1,49 @@ +/* + * Copyright 2026 Dawson Hessler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package dev.brighten.antivpn.database.version; +import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.database.local.H2VPN; +import dev.brighten.antivpn.database.local.version.First; +import dev.brighten.antivpn.database.local.version.Second; +import dev.brighten.antivpn.database.sql.utils.MySQL; +import java.io.File; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.Optional; public interface H2Version extends Version { - List versions = new ArrayList<>(); + H2Version[] versions = new H2Version[] {new First(), new Second()}; - static void registerVersions() { + default void backupDatabase() { + File dataFolder = new File(AntiVPN.getInstance().getPluginFolder(), "databases"); + if(!dataFolder.exists()) { + return; + } + + List files = new ArrayList<>(List.of(Optional.ofNullable(dataFolder.listFiles()).orElse(new File[0]))); + files.sort(Comparator.comparingLong(File::lastModified)); + + for (File file : files) { + MySQL.backupOldDB(file, dataFolder); + } } } diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/database/version/Version.java b/Common/Source/src/main/java/dev/brighten/antivpn/database/version/Version.java index 11b9217..b3b9876 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/database/version/Version.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/database/version/Version.java @@ -1,7 +1,25 @@ +/* + * Copyright 2026 Dawson Hessler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package dev.brighten.antivpn.database.version; +import java.sql.SQLException; + public interface Version { - void update(DB database) throws Exception; + void update(DB database) throws SQLException; int versionNumber(); boolean needsUpdate(DB database); } \ No newline at end of file diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/utils/IpUtils.java b/Common/Source/src/main/java/dev/brighten/antivpn/utils/IpUtils.java new file mode 100644 index 0000000..bb982ec --- /dev/null +++ b/Common/Source/src/main/java/dev/brighten/antivpn/utils/IpUtils.java @@ -0,0 +1,109 @@ +/* + * Copyright 2026 Dawson Hessler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.brighten.antivpn.utils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Optional; + +public class IpUtils { + public static Optional getIpDecimal(String address) { + try { + InetAddress inet = InetAddress.getByName(address); + + if(inet instanceof Inet4Address) { + return Optional.of(BigDecimal.valueOf(ipv4ToLong(address))); + } return Optional.of(new BigDecimal(ipv6ToDecimalFormat(address))); + } catch(Exception e) { + return Optional.empty(); + } + } + + public static long ipv4ToLong(String address) { + String[] addrArray = address.split("\\."); + + long ipDecimal = 0; + + for (int i = 0; i < addrArray.length; i++) { + + int power = 3 - i; + ipDecimal += ((Integer.parseInt(addrArray[i]) % 256 * Math.pow(256, power))); + } + + return ipDecimal; + } + + public static String getIpv4(long ip) { + StringBuilder sb = new StringBuilder(15); + + for (int i = 0; i < 4; i++) { + sb.insert(0, ip & 0xff); + + if (i < 3) { + sb.insert(0, '.'); + } + + ip >>= 8; + } + + return sb.toString(); + } + + public static boolean isIpv4(BigDecimal ip) { + return ip.compareTo(BigDecimal.valueOf(4294967295L)) <= 0; + } + + public static boolean isIpv6(BigDecimal ip) { + return ip.compareTo(BigDecimal.valueOf(4294967295L)) > 0; + } + public static boolean isIpv4(String ip) { + return ip.matches("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$"); + } + + public static boolean isNotIp(String ip) { + return !isIpv4(ip) && !isIpv6(ip); + } + + public static boolean isIpv6(String ip) { + return ip.matches("^([0-9a-fA-F]{1,4}:){7}([0-9a-fA-F]{1,4}|:)$|^(([0-9a-fA-F]{1,4}:){0,6}([0-9a-fA-F]{1,4}|:))?(::([0-9a-fA-F]{1,4}:){0,5}([0-9a-fA-F]{1,4}|:))?$"); + } + + public static String getIpv4(BigDecimal ip) { + try { + return Inet4Address.getByAddress(ip.toBigInteger().toByteArray()).getHostAddress(); + } catch (UnknownHostException e) { + return "Error"; + } + } + + public static String getIpv6(BigDecimal ip) { + try { + return Inet6Address.getByAddress(ip.toBigInteger().toByteArray()).getHostAddress(); + } catch (UnknownHostException e) { + return "Error"; + } + } + + public static BigInteger ipv6ToDecimalFormat(String ipAddress) throws UnknownHostException { + return new BigInteger(1, Inet6Address.getByName(ipAddress).getAddress()); + } + +} diff --git a/Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java b/Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java index 242379b..af4c80a 100644 --- a/Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java +++ b/Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java @@ -49,7 +49,7 @@ public class VelocityListener extends VPNExecutor { LegacyComponentSerializer.builder() .character('&') .build().deserialize(AntiVPN.getInstance().getVpnConfig() - .countryVanillaKickReason() + .getCountryVanillaKickReason() .replace("%player%", event.getPlayer().getUsername()) .replace("%country%", instantResult.response().getCountryName()) .replace("%code%", instantResult.response().getCountryCode())))); @@ -59,7 +59,7 @@ public class VelocityListener extends VPNExecutor { event.setResult(ResultedEvent.ComponentResult.denied(LegacyComponentSerializer.builder() .character('&') .build().deserialize(AntiVPN.getInstance().getVpnConfig() - .getKickString() + .getKickMessage() .replace("%player%", event.getPlayer().getUsername()) .replace("%country%", instantResult.response().getCountryName()) .replace("%code%", instantResult.response().getCountryCode())))); @@ -97,14 +97,14 @@ public class VelocityListener extends VPNExecutor { //In case the user wants to run their own commands instead of using the // built in kicking - if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { + if(AntiVPN.getInstance().getVpnConfig().isKickPlayers()) { switch (checkResult.resultType()) { case DENIED_PROXY -> VelocityPlugin.INSTANCE.getServer().getScheduler() .buildTask(VelocityPlugin.INSTANCE.getPluginInstance(), () -> event.getPlayer().disconnect(LegacyComponentSerializer.builder() .character('&') .build().deserialize(AntiVPN.getInstance().getVpnConfig() - .getKickString() + .getKickMessage() .replace("%player%", event.getPlayer().getUsername()) .replace("%country%", result.getCountryName()) .replace("%code%", result.getCountryCode())))) @@ -114,7 +114,7 @@ public class VelocityListener extends VPNExecutor { event.getPlayer().disconnect(LegacyComponentSerializer.builder() .character('&') .build().deserialize(AntiVPN.getInstance().getVpnConfig() - .countryVanillaKickReason() + .getCountryVanillaKickReason() .replace("%player%", event.getPlayer().getUsername()) .replace("%country%", result.getCountryName()) .replace("%code%", result.getCountryCode()))))