From 11ef7e8d5029107b69158393b5850c6522ab09e6 Mon Sep 17 00:00:00 2001 From: Dawson Date: Fri, 20 Feb 2026 10:15:59 -0500 Subject: [PATCH] Fixed CIDR notations and the ability for mongo to properly grab CIDRs --- .../command/impl/AllowlistCommand.java | 2 + .../antivpn/database/local/H2VPN.java | 10 +- .../antivpn/database/local/version/Third.java | 115 ++++++++++++++++++ .../antivpn/database/mongo/MongoVPN.java | 4 +- .../database/mongo/version/MongoThird.java | 108 ++++++++++++++++ .../antivpn/database/version/Version.java | 8 +- .../dev/brighten/antivpn/utils/MiscUtils.java | 42 +++++++ 7 files changed, 281 insertions(+), 8 deletions(-) create mode 100644 Common/Source/src/main/java/dev/brighten/antivpn/database/local/version/Third.java create mode 100644 Common/Source/src/main/java/dev/brighten/antivpn/database/mongo/version/MongoThird.java diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/command/impl/AllowlistCommand.java b/Common/Source/src/main/java/dev/brighten/antivpn/command/impl/AllowlistCommand.java index 53174c0..c16df14 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/command/impl/AllowlistCommand.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/command/impl/AllowlistCommand.java @@ -78,6 +78,8 @@ public class AllowlistCommand extends Command { String safeSearch = search != null ? search.replace("&", "") : null; boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled(); + AntiVPN.getInstance().getExecutor().log("Is Database Enabled: %s", databaseEnabled ? "yes" : "no"); + List uuids = databaseEnabled ? AntiVPN.getInstance().getDatabase().getAllWhitelisted() : new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelisted()); 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 b0a1971..3235abb 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 @@ -207,7 +207,7 @@ public class H2VPN implements VPNDatabase { return; try(var statement = Query.prepare("insert into `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) values (?, ?, ?)") - .append(cidr.toString()).append(cidr.getStartIpInt()).append(cidr.getEndIpInt())) { + .append(cidr.getCidr()).append(cidr.getStartIpInt()).append(cidr.getEndIpInt())) { statement.execute(); } catch (SQLException e) { @@ -220,7 +220,7 @@ public class H2VPN implements VPNDatabase { if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return; - try(var statement = Query.prepare("delete from `whitelisted-ranges` where `cidr_string` = ?").append(cidr.toString())) { + try(var statement = Query.prepare("delete from `whitelisted-ranges` where `cidr_string` = ?").append(cidr.getCidr())) { statement.execute(); } catch (SQLException e) { @@ -253,7 +253,11 @@ public class H2VPN implements VPNDatabase { try(var statement = Query.prepare("select `cidr_string`, `ip_start`, `ip_end` from `whitelisted-ranges`")) { statement.execute(set -> { try { - ips.add(new CIDRUtils(set.getString("cidr_string"))); + String cidrString = set.getString("cidr_string"); + + AntiVPN.getInstance().getExecutor().log("CIDR String: %s", cidrString); + ips.add(new CIDRUtils(cidrString)); + } catch (UnknownHostException e) { AntiVPN.getInstance().getExecutor() .logException("Could not format ip " diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/database/local/version/Third.java b/Common/Source/src/main/java/dev/brighten/antivpn/database/local/version/Third.java new file mode 100644 index 0000000..cd56576 --- /dev/null +++ b/Common/Source/src/main/java/dev/brighten/antivpn/database/local/version/Third.java @@ -0,0 +1,115 @@ +/* + * 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.DatabaseException; +import dev.brighten.antivpn.database.VPNDatabase; +import dev.brighten.antivpn.database.sql.utils.Query; +import dev.brighten.antivpn.database.version.Version; +import dev.brighten.antivpn.utils.CIDRUtils; +import dev.brighten.antivpn.utils.MiscUtils; + +import java.math.BigInteger; +import java.net.UnknownHostException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; + +public class Third implements Version { + @Override + public void update(VPNDatabase database) throws DatabaseException { + List ipRanges = new ArrayList<>(); + List rangesToInsert = new ArrayList<>(); + List rangesToRemove = new ArrayList<>(); + try (var preparedQuery = Query.prepare("select ip_start, ip_end from `whitelist-ranges`")) { + preparedQuery.execute(set -> { + BigInteger start = set.getBigDecimal("ip_start").toBigInteger(); + BigInteger end = set.getBigDecimal("ip_end").toBigInteger(); + + try { + var range = MiscUtils.rangeToCidrs(start, end); + + if(range.size() > 1) { + rangesToRemove.add(new BigInteger[]{start, end}); + rangesToInsert.addAll(range); + AntiVPN.getInstance().getExecutor().log(Level.WARNING, "Found multiple CIDR ranges for whitelist range for %s, %s!", start, end); + } else ipRanges.addAll(range); + } catch (UnknownHostException e) { + AntiVPN.getInstance().getExecutor().logException( + String.format("Could not convert ip range to CIDR! %s, %s", start, end), e); + } + }); + } catch (SQLException e) { + AntiVPN.getInstance().getExecutor().logException("Could not get all whitelisted ranges due to SQL error.", e); + } + + AntiVPN.getInstance().getExecutor().log("Inserting %s new ranges into database...", rangesToInsert.size()); + + for (CIDRUtils cidr : rangesToInsert) { + try(var statement = Query.prepare("insert into `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) values (?, ?, ?)") + .append(cidr.getCidr()).append(cidr.getStartIpInt()).append(cidr.getEndIpInt())) { + statement.execute(); + } catch (SQLException e) { + AntiVPN.getInstance().getExecutor().logException("Could not add cidr '" + cidr + "' to whitelist due to SQL error.", e); + } + } + + AntiVPN.getInstance().getExecutor().log("Removing %s old ranges from database...", rangesToRemove.size()); + + for (BigInteger[] range : rangesToRemove) { + try(var statement = Query.prepare("delete from `whitelisted-ranges` where `ip_start` = ? and `ip_end` = ?")) { + statement.append(range[0]).append(range[1]).execute(); + } catch (SQLException e) { + AntiVPN.getInstance().getExecutor().logException("Could not remove cidr range '" + range[0] + ", " + range[1] + "' from whitelist due to SQL error.", e); + } + } + + AntiVPN.getInstance().getExecutor().log("Updating %s ranges to proper CIDR notation with the database", ipRanges.size()); + + for (CIDRUtils cidr : ipRanges) { + try(var statement = Query.prepare("update `whitelisted-ranges` set `cidr_string` = ? where `ip_start` = ? and `ip_end` = ?")) { + statement.append(cidr.getCidr()).append(cidr.getStartIpInt()).append(cidr.getEndIpInt()).execute(); + } catch (SQLException e) { + AntiVPN.getInstance().getExecutor().logException("Could not update cidr '" + cidr + "' to proper CIDR notation in whitelist due to SQL error.", e); + } + } + + try (var preparedStatement = Query.prepare("INSERT INTO `database_version` (`version`) VALUES (?)").append(versionNumber())) { + preparedStatement.execute(); + } catch (SQLException e) { + AntiVPN.getInstance().getExecutor().logException("Could not update database version to 2 due to SQL error.", e); + } + } + + @Override + public int versionNumber() { + return 2; + } + + @Override + public boolean needsUpdate(VPNDatabase database) { + try (var statement = Query.prepare("select * from `database_version` where version = 2")) { + try(var set = statement.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 cd8fe24..87fb166 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 @@ -183,7 +183,7 @@ public class MongoVPN implements VPNDatabase { Document doc = new Document("setting", "whitelist"); doc.append("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt()))); doc.append("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt()))); - doc.append("cidr_string", cidr.toString()); + doc.append("cidr_string", cidr.getCidr()); settingsDocument.insertOne(doc); } @@ -210,7 +210,7 @@ public class MongoVPN implements VPNDatabase { public List getAllWhitelistedIps() { List ips = new ArrayList<>(); settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"), - Filters.exists("ip"))).forEach((Consumer) doc -> { + Filters.exists("cidr_string"))).forEach((Consumer) doc -> { try { var cidr = new CIDRUtils(doc.getString("cidr_string")); ips.add(cidr); diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/database/mongo/version/MongoThird.java b/Common/Source/src/main/java/dev/brighten/antivpn/database/mongo/version/MongoThird.java new file mode 100644 index 0000000..daec1fb --- /dev/null +++ b/Common/Source/src/main/java/dev/brighten/antivpn/database/mongo/version/MongoThird.java @@ -0,0 +1,108 @@ +/* + * 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.mongo.version; + +import com.mongodb.client.model.Filters; +import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.database.DatabaseException; +import dev.brighten.antivpn.database.mongo.MongoVPN; +import dev.brighten.antivpn.database.version.Version; +import dev.brighten.antivpn.utils.CIDRUtils; +import dev.brighten.antivpn.utils.MiscUtils; +import org.bson.Document; +import org.bson.types.Decimal128; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.logging.Level; + +public class MongoThird implements Version { + @Override + public void update(MongoVPN database) throws DatabaseException { + List ipRanges = new ArrayList<>(); + List rangesToInsert = new ArrayList<>(); + List rangesToRemove = new ArrayList<>(); + database.settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"), Filters.exists("cidr_string"))) + .forEach((Consumer) doc -> { + BigInteger start = doc.get("ip_start", Decimal128.class).bigDecimalValue().toBigInteger(); + BigInteger end = doc.get("ip_end", Decimal128.class).bigDecimalValue().toBigInteger(); + + try { + var range = MiscUtils.rangeToCidrs(start, end); + + if(range.size() > 1) { + rangesToRemove.add(new BigInteger[]{start, end}); + rangesToInsert.addAll(range); + AntiVPN.getInstance().getExecutor().log(Level.WARNING, "Found multiple CIDR ranges for whitelist range for %s, %s!", start, end); + } else ipRanges.addAll(range); + } catch (UnknownHostException e) { + AntiVPN.getInstance().getExecutor().logException( + String.format("Could not convert ip range to CIDR! %s, %s", start, end), e); + } + }); + + if(!rangesToInsert.isEmpty()) { + AntiVPN.getInstance().getExecutor().log("Inserting %s new ranges into database...", rangesToInsert.size()); + var documentsToInsert = rangesToInsert.stream().map(cidr -> { + Document doc = new Document("setting", "whitelist"); + doc.append("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt()))); + doc.append("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt()))); + doc.append("cidr_string", cidr.getCidr()); + + return doc; + }).toList(); + + database.settingsDocument.insertMany(documentsToInsert); + } + if(!rangesToRemove.isEmpty()) { + AntiVPN.getInstance().getExecutor().log("Removing %s old ranges from database...", rangesToRemove.size()); + rangesToRemove.forEach(range -> database.settingsDocument + .deleteMany(Filters.and( + Filters.gte("ip_start", new Decimal128(new BigDecimal(range[0]))), + Filters.lte("ip_end", new Decimal128(new BigDecimal(range[1])))))); + } + + if(!ipRanges.isEmpty()) { + AntiVPN.getInstance().getExecutor().log("Updating %s CIDRs in database with proper notation...", ipRanges.size()); + + ipRanges.forEach(cidr -> database.settingsDocument + .updateMany(Filters.and(Filters.eq("setting", "whitelist"), + Filters.eq("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt()))), + Filters.eq("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())))), + new Document("$set", new Document("cidr_string", cidr.getCidr())))); + } + + var versionCollect = database.antivpnDatabase.getCollection("version"); + versionCollect.insertOne(new Document("version", versionNumber())); + } + + @Override + public int versionNumber() { + return 2; + } + + @Override + public boolean needsUpdate(MongoVPN database) { + var versionCollect = database.antivpnDatabase.getCollection("version"); + + return versionCollect.find(Filters.eq("version", versionNumber())).first() == null; + } +} 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 f9340b9..2c714b4 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 @@ -20,9 +20,11 @@ import dev.brighten.antivpn.database.DatabaseException; 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.local.version.Third; import dev.brighten.antivpn.database.mongo.MongoVPN; import dev.brighten.antivpn.database.mongo.version.MongoFirst; import dev.brighten.antivpn.database.mongo.version.MongoSecond; +import dev.brighten.antivpn.database.mongo.version.MongoThird; import dev.brighten.antivpn.database.sql.MySqlVPN; import dev.brighten.antivpn.database.sql.version.MySQLFirst; @@ -32,7 +34,7 @@ public interface Version { int versionNumber(); boolean needsUpdate(DB database); - Version[] mongoDbVersions = new Version[] {new MongoFirst(), new MongoSecond()}; - Version[] mysqlVersions = new Version[] {new MySQLFirst(), new Second()}; - Version[] h2Versions = new Version[] {new First(), new Second()}; + Version[] mongoDbVersions = new Version[] {new MongoFirst(), new MongoSecond(), new MongoThird()}; + Version[] mysqlVersions = new Version[] {new MySQLFirst(), new Second(), new Third()}; + Version[] h2Versions = new Version[] {new First(), new Second(), new Third()}; } \ No newline at end of file diff --git a/Common/Source/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java b/Common/Source/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java index 28d0735..4618946 100644 --- a/Common/Source/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java +++ b/Common/Source/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java @@ -22,6 +22,11 @@ import dev.brighten.antivpn.utils.json.JSONObject; import dev.brighten.antivpn.utils.json.JsonReader; import java.io.*; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import java.util.concurrent.ThreadFactory; import java.util.regex.Pattern; @@ -72,6 +77,43 @@ public class MiscUtils { }; } + public static List rangeToCidrs(BigInteger start, BigInteger end) throws UnknownHostException { + List cidrs = new ArrayList<>(); + + while (start.compareTo(end) <= 0) { + // Find the number of trailing zero bits — this determines max block size alignment + int trailingZeros = start.equals(BigInteger.ZERO) + ? 128 // handle the edge case + : start.getLowestSetBit(); + + // Find the largest block that fits + BigInteger remaining = end.subtract(start).add(BigInteger.ONE); + int maxBits = remaining.bitLength() - 1; + + int blockBits = Math.min(trailingZeros, maxBits); + int prefixLen = 32 - blockBits; // use 128 for IPv6 + + // Build the CIDR string + byte[] addrBytes = toFixedLengthBytes(start, 4); // use 16 for IPv6 + String cidr = InetAddress.getByAddress(addrBytes).getHostAddress() + "/" + prefixLen; + cidrs.add(new CIDRUtils(cidr)); + + // Advance past this block + start = start.add(BigInteger.ONE.shiftLeft(blockBits)); + } + + return cidrs; + } + + private static byte[] toFixedLengthBytes(BigInteger value, int length) { + byte[] raw = value.toByteArray(); + byte[] result = new byte[length]; + int srcPos = Math.max(0, raw.length - length); + int destPos = Math.max(0, length - raw.length); + System.arraycopy(raw, srcPos, result, destPos, Math.min(raw.length, length)); + return result; + } + public static UUID lookupUUID(String playername) { try { JSONObject object = JsonReader