mirror of
https://github.com/funkemunky/AntiVPN.git
synced 2026-06-09 05:07:38 +00:00
Merge branch 'master' into 57-feature-allow-subnets-to-be-whitelisted
# Conflicts: # Bukkit/pom.xml # Bungee/pom.xml # Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java # Common/pom.xml # Common/src/main/java/dev/brighten/antivpn/AntiVPN.java # Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java # Common/src/main/java/dev/brighten/antivpn/database/local/H2VPN.java # Common/src/main/java/dev/brighten/antivpn/database/mongo/MongoVPN.java # Common/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java # Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java # Common/src/main/java/dev/brighten/antivpn/database/sql/utils/Query.java # Common/src/main/java/dev/brighten/antivpn/utils/StringUtil.java # Sponge/pom.xml # Universal/pom.xml # Velocity/pom.xml # Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java # pom.xml
This commit is contained in:
@@ -1,18 +1,30 @@
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@Getter
|
||||
public abstract class APIPlayer {
|
||||
private final UUID uuid;
|
||||
private final String name;
|
||||
private final InetAddress ip;
|
||||
@Setter
|
||||
private boolean alertsEnabled;
|
||||
|
||||
private static final Cache<String, CheckResult> checkResultCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||
.maximumSize(2000)
|
||||
.build();
|
||||
|
||||
public APIPlayer(UUID uuid, String name, InetAddress ip) {
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
@@ -25,12 +37,65 @@ public abstract class APIPlayer {
|
||||
|
||||
public abstract boolean hasPermission(String permission);
|
||||
|
||||
public void setAlertsEnabled(boolean alertsEnabled) {
|
||||
this.alertsEnabled = alertsEnabled;
|
||||
}
|
||||
|
||||
public void updateAlertsState() {
|
||||
//Updating into database so its synced across servers and saved on logout.
|
||||
AntiVPN.getInstance().getDatabase().updateAlertsState(uuid, alertsEnabled);
|
||||
}
|
||||
|
||||
public CheckResult checkPlayer(Consumer<CheckResult> onKick) {
|
||||
if (hasPermission("antivpn.bypass") //Has bypass permission
|
||||
//Is exempt
|
||||
|| (uuid != null && AntiVPN.getInstance().getExecutor().isWhitelisted(uuid))
|
||||
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|
||||
|| AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress())
|
||||
|| AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream()
|
||||
.anyMatch(name::startsWith)) return new CheckResult(null, ResultType.WHITELISTED);
|
||||
|
||||
CheckResult cachedResult = checkResultCache.getIfPresent(ip.getHostAddress());
|
||||
|
||||
if(cachedResult != null) {
|
||||
if(cachedResult.response().getIp().equals(ip.getHostAddress())) {
|
||||
AntiVPN.getInstance().getExecutor().log(Level.FINE, "Cached result for " + ip.getHostAddress() + " is " + cachedResult.resultType());
|
||||
return cachedResult;
|
||||
}
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().checkIp(ip.getHostAddress())
|
||||
.thenAccept(result -> {
|
||||
if(!result.isSuccess()) {
|
||||
AntiVPN.getInstance().getExecutor().log(Level.WARNING, "The API query was not a success! " +
|
||||
"You may need to upgrade your license on " +
|
||||
"https://funkemunky.cc/shop");
|
||||
}
|
||||
// If the countryList() size is zero, no need to check.
|
||||
// Running country check first
|
||||
CheckResult checkResult;
|
||||
if (!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty()
|
||||
&& !((uuid != null && AntiVPN.getInstance().getExecutor()
|
||||
.isWhitelisted(uuid))
|
||||
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|
||||
|| AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress()))
|
||||
// This bit of code will decide whether or not to kick the player
|
||||
// If it contains the code and it is set to whitelist, it will not kick
|
||||
// as they are equal and vise versa. However, if the contains does not match
|
||||
// the state, it will kick.
|
||||
&& AntiVPN.getInstance().getVpnConfig().countryList()
|
||||
.contains(result.getCountryCode())
|
||||
!= AntiVPN.getInstance().getVpnConfig().whitelistCountries()) {
|
||||
//Using our built in kicking system if no commands are configured
|
||||
checkResult = new CheckResult(result, ResultType.DENIED_COUNTRY);
|
||||
} else if (result.isProxy()) {
|
||||
checkResult = new CheckResult(result, ResultType.DENIED_PROXY);
|
||||
} else {
|
||||
checkResult = new CheckResult(result, ResultType.ALLOWED);
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log(Level.FINE, "Result for " + ip.getHostAddress() + " is " + checkResult.resultType());
|
||||
|
||||
checkResultCache.put(ip.getHostAddress(), checkResult);
|
||||
onKick.accept(checkResult);
|
||||
AntiVPN.getInstance().checked++;
|
||||
});
|
||||
return new CheckResult(null, ResultType.UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
|
||||
public record CheckResult(VPNResponse response, ResultType resultType) {
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
public class OfflinePlayer extends APIPlayer {
|
||||
|
||||
public OfflinePlayer(UUID uuid, String name, InetAddress ip) {
|
||||
super(uuid, name, ip);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kickPlayer(String reason) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public enum ResultType {
|
||||
ALLOWED(false),
|
||||
WHITELISTED(false),
|
||||
DENIED_COUNTRY(true),
|
||||
DENIED_PROXY(true),
|
||||
UNKNOWN(false);
|
||||
|
||||
@Getter
|
||||
private final boolean shouldBlock;
|
||||
|
||||
ResultType(boolean shouldBlock) {
|
||||
this.shouldBlock = shouldBlock;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import dev.brighten.antivpn.utils.Tuple;
|
||||
import dev.brighten.antivpn.utils.json.JSONException;
|
||||
import dev.brighten.antivpn.web.FunkemunkyAPI;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
@@ -10,11 +10,10 @@ import lombok.Getter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public abstract class VPNExecutor {
|
||||
@@ -28,10 +27,8 @@ public abstract class VPNExecutor {
|
||||
@Getter
|
||||
private final Set<String> whitelistedIps = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
private final Cache<String, VPNResponse> responseCache = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(20, TimeUnit.MINUTES)
|
||||
.maximumSize(4000)
|
||||
.build();
|
||||
@Getter
|
||||
private final List<Tuple<CheckResult, UUID>> toKick = Collections.synchronizedList(new LinkedList<>());
|
||||
|
||||
public abstract void registerListeners();
|
||||
|
||||
@@ -43,12 +40,75 @@ public abstract class VPNExecutor {
|
||||
|
||||
public abstract void log(String log, Object... objects);
|
||||
|
||||
public abstract void logException(String message, Exception ex);
|
||||
public abstract void logException(String message, Throwable ex);
|
||||
|
||||
public void logException(Exception ex) {
|
||||
public abstract void runCommand(String command);
|
||||
|
||||
public void logException(Throwable ex) {
|
||||
logException("An exception occurred: " + ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
public void startKickChecks() {
|
||||
threadExecutor.scheduleAtFixedRate(() -> {
|
||||
synchronized (toKick) {
|
||||
if(toKick.isEmpty()) return;
|
||||
|
||||
Iterator<Tuple<CheckResult, UUID>> i = toKick.iterator();
|
||||
|
||||
while(i.hasNext()) {
|
||||
var toCheck = i.next();
|
||||
|
||||
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(toCheck.second());
|
||||
|
||||
if(player.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
handleKickingOfPlayer(toCheck.first(), player.get());
|
||||
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}, 8, 2, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void handleKickingOfPlayer(CheckResult result, APIPlayer player) {
|
||||
if (AntiVPN.getInstance().getVpnConfig().alertToStaff()) AntiVPN.getInstance().getPlayerExecutor()
|
||||
.getOnlinePlayers()
|
||||
.stream()
|
||||
.filter(APIPlayer::isAlertsEnabled)
|
||||
.forEach(pl ->
|
||||
pl.sendMessage(StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(dev.brighten.antivpn.AntiVPN.getInstance().getVpnConfig()
|
||||
.alertMessage(), player, result.response()))));
|
||||
|
||||
if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) {
|
||||
switch (result.resultType()) {
|
||||
case DENIED_PROXY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
||||
.getKickString(), player, result.response()));
|
||||
case DENIED_COUNTRY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
||||
.countryVanillaKickReason(), player, result.response()));
|
||||
}
|
||||
}
|
||||
|
||||
if(!AntiVPN.getInstance().getVpnConfig().runCommands()) return;
|
||||
|
||||
switch (result.resultType()) {
|
||||
case DENIED_PROXY -> {
|
||||
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
|
||||
runCommand(StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(command, player, result.response())));
|
||||
}
|
||||
}
|
||||
case DENIED_COUNTRY -> {
|
||||
for (String command : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
|
||||
runCommand(StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(command, player, result.response())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isWhitelisted(UUID uuid) {
|
||||
if(AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) {
|
||||
return AntiVPN.getInstance().getDatabase().isWhitelisted(uuid);
|
||||
@@ -63,43 +123,32 @@ public abstract class VPNExecutor {
|
||||
return whitelistedIps.contains(ip);
|
||||
}
|
||||
|
||||
public void checkIp(String ip, boolean cachedResults, Consumer<VPNResponse> result) {
|
||||
threadExecutor.execute(() -> {
|
||||
if(cachedResults) {
|
||||
public CompletableFuture<VPNResponse> checkIp(String ip) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
Optional<VPNResponse> cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(ip);
|
||||
|
||||
if(cachedRes.isPresent()) {
|
||||
return cachedRes.get();
|
||||
}
|
||||
else {
|
||||
try {
|
||||
result.accept(responseCache.get(ip, () -> checkIp(ip)));
|
||||
} catch (ExecutionException e) {
|
||||
log("Failed to process checkIp() method! Reason: " + e.getMessage());
|
||||
result.accept(VPNResponse.FAILED_RESPONSE);
|
||||
VPNResponse response = FunkemunkyAPI
|
||||
.getVPNResponse(ip, AntiVPN.getInstance().getVpnConfig().getLicense(), true);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
AntiVPN.getInstance().getDatabase().cacheResponse(response);
|
||||
} else {
|
||||
log("Query to VPN API failed! Reason: " + response.getFailureReason());
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (JSONException | IOException e) {
|
||||
log("Query to VPN API failed! Reason: " + e.getMessage());
|
||||
return VPNResponse.FAILED_RESPONSE;
|
||||
}
|
||||
} else {
|
||||
result.accept(checkIp(ip));
|
||||
}
|
||||
});
|
||||
}, threadExecutor);
|
||||
}
|
||||
|
||||
public VPNResponse checkIp(String ip) {
|
||||
Optional<VPNResponse> cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(ip);
|
||||
|
||||
if(cachedRes.isPresent()) {
|
||||
return cachedRes.get();
|
||||
}
|
||||
else {
|
||||
try {
|
||||
VPNResponse response = FunkemunkyAPI
|
||||
.getVPNResponse(ip, AntiVPN.getInstance().getVpnConfig().getLicense(), true);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
AntiVPN.getInstance().getDatabase().cacheResponse(response);
|
||||
} else {
|
||||
log("Query to VPN API failed! Reason: " + response.getFailureReason());
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (JSONException | IOException e) {
|
||||
log("Query to VPN API failed! Reason: " + e.getMessage());
|
||||
return VPNResponse.FAILED_RESPONSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
public abstract void disablePlugin();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user