package me.egg82.antivpn.commands.internal; import co.aikar.commands.CommandIssuer; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import inet.ipaddr.IPAddress; import inet.ipaddr.IPAddressString; import java.net.InetAddress; import java.net.UnknownHostException; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attributes; import javax.naming.directory.InitialDirContext; import me.egg82.antivpn.APIException; import me.egg82.antivpn.VPNAPI; import me.egg82.antivpn.enums.Message; import me.egg82.antivpn.utils.ConfigUtil; import me.egg82.antivpn.utils.ValidationUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ScoreCommand implements Runnable { private static final Logger logger = LoggerFactory.getLogger(ScoreCommand.class); private final CommandIssuer issuer; private final String source; private final VPNAPI api = VPNAPI.getInstance(); private static final DecimalFormat format = new DecimalFormat("##0.00"); public ScoreCommand(CommandIssuer issuer, String source) { this.issuer = issuer; this.source = source; } public void run() { issuer.sendInfo(Message.SCORE__BEGIN, "{source}", source); issuer.sendInfo(Message.SCORE__TYPE, "{type}", "NordVPN"); test(issuer, source, "NordVPN", getNordVPNIPs()); issuer.sendInfo(Message.SCORE__SLEEP); try { Thread.sleep(60000L); } catch (InterruptedException ignored) { Thread.currentThread().interrupt(); } issuer.sendInfo(Message.SCORE__TYPE, "{type}", "Cryptostorm"); test(issuer, source, "Cryptostorm", getCryptostormIPs()); issuer.sendInfo(Message.SCORE__SLEEP); try { Thread.sleep(60000L); } catch (InterruptedException ignored) { Thread.currentThread().interrupt(); } issuer.sendInfo(Message.SCORE__TYPE, "{type}", "random home IPs"); test(issuer, source, "Random home IP", getHomeIPs(), true); issuer.sendInfo(Message.SCORE__END, "{source}", source); } private void test(CommandIssuer issuer, String source, String vpnName, Set<String> ips) { test(issuer, source, vpnName, ips, false); } private void test(CommandIssuer issuer, String source, String vpnName, Set<String> ips, boolean flipResult) { if (ConfigUtil.getDebugOrFalse()) { logger.info("Testing against " + vpnName); } double error = 0.0d; double good = 0.0d; int i = 0; for (String ip : ips) { i++; try { if (source.equalsIgnoreCase("getipintel")) { Thread.sleep(5000L); // 15req/min max, so every 4 seconds. 5 to be safe. } else { Thread.sleep(1000L); } } catch (IllegalArgumentException ex) { logger.error(ex.getMessage(), ex); } catch (InterruptedException ex) { logger.error(ex.getMessage(), ex); Thread.currentThread().interrupt(); } if (ConfigUtil.getDebugOrFalse()) { logger.info("Testing " + ip + " (" + i + "/" + ips.size() + ")"); } boolean result; try { result = api.getSourceResult(ip, source); } catch (APIException ex) { if (ex.isHard()) { logger.error(ex.getMessage(), ex); continue; } error++; continue; } if ((!flipResult && result) || (flipResult && !result)) { good++; } } if (error > 0) { issuer.sendInfo(Message.SCORE__ERROR, "{source}", source, "{type}", vpnName, "{percent}", format.format((error / ips.size()) * 100.0d)); } issuer.sendInfo(Message.SCORE__SCORE, "{source}", source, "{type}", vpnName, "{percent}", format.format((good / ips.size()) * 100.0d)); } private Set<String> getNordVPNIPs() { Set<String> dns = new HashSet<>(); dns.addAll(validNordVPN.get("al{}.nordvpn.com")); dns.addAll(validNordVPN.get("ar{}.nordvpn.com")); dns.addAll(validNordVPN.get("au{}.nordvpn.com")); dns.addAll(validNordVPN.get("at{}.nordvpn.com")); dns.addAll(validNordVPN.get("be{}.nordvpn.com")); dns.addAll(validNordVPN.get("ba{}.nordvpn.com")); dns.addAll(validNordVPN.get("br{}.nordvpn.com")); dns.addAll(validNordVPN.get("bg{}.nordvpn.com")); dns.addAll(validNordVPN.get("ca{}.nordvpn.com")); dns.addAll(validNordVPN.get("cl{}.nordvpn.com")); dns.addAll(validNordVPN.get("cr{}.nordvpn.com")); dns.addAll(validNordVPN.get("hr{}.nordvpn.com")); dns.addAll(validNordVPN.get("cy{}.nordvpn.com")); dns.addAll(validNordVPN.get("cz{}.nordvpn.com")); dns.addAll(validNordVPN.get("dk{}.nordvpn.com")); dns.addAll(validNordVPN.get("ee{}.nordvpn.com")); dns.addAll(validNordVPN.get("fi{}.nordvpn.com")); dns.addAll(validNordVPN.get("fr{}.nordvpn.com")); dns.addAll(validNordVPN.get("ge{}.nordvpn.com")); dns.addAll(validNordVPN.get("de{}.nordvpn.com")); dns.addAll(validNordVPN.get("gr{}.nordvpn.com")); dns.addAll(validNordVPN.get("hk{}.nordvpn.com")); dns.addAll(validNordVPN.get("hu{}.nordvpn.com")); dns.addAll(validNordVPN.get("is{}.nordvpn.com")); dns.addAll(validNordVPN.get("in{}.nordvpn.com")); dns.addAll(validNordVPN.get("id{}.nordvpn.com")); dns.addAll(validNordVPN.get("il{}.nordvpn.com")); dns.addAll(validNordVPN.get("it{}.nordvpn.com")); dns.addAll(validNordVPN.get("jp{}.nordvpn.com")); dns.addAll(validNordVPN.get("lv{}.nordvpn.com")); dns.addAll(validNordVPN.get("lu{}.nordvpn.com")); dns.addAll(validNordVPN.get("my{}.nordvpn.com")); dns.addAll(validNordVPN.get("mx{}.nordvpn.com")); dns.addAll(validNordVPN.get("md{}.nordvpn.com")); dns.addAll(validNordVPN.get("nl{}.nordvpn.com")); dns.addAll(validNordVPN.get("nz{}.nordvpn.com")); dns.addAll(validNordVPN.get("mk{}.nordvpn.com")); dns.addAll(validNordVPN.get("no{}.nordvpn.com")); dns.addAll(validNordVPN.get("pl{}.nordvpn.com")); dns.addAll(validNordVPN.get("pt{}.nordvpn.com")); dns.addAll(validNordVPN.get("ro{}.nordvpn.com")); dns.addAll(validNordVPN.get("rs{}.nordvpn.com")); dns.addAll(validNordVPN.get("sg{}.nordvpn.com")); dns.addAll(validNordVPN.get("sk{}.nordvpn.com")); dns.addAll(validNordVPN.get("si{}.nordvpn.com")); dns.addAll(validNordVPN.get("za{}.nordvpn.com")); dns.addAll(validNordVPN.get("kr{}.nordvpn.com")); dns.addAll(validNordVPN.get("es{}.nordvpn.com")); dns.addAll(validNordVPN.get("se{}.nordvpn.com")); dns.addAll(validNordVPN.get("ch{}.nordvpn.com")); dns.addAll(validNordVPN.get("tw{}.nordvpn.com")); dns.addAll(validNordVPN.get("th{}.nordvpn.com")); dns.addAll(validNordVPN.get("tr{}.nordvpn.com")); dns.addAll(validNordVPN.get("ua{}.nordvpn.com")); dns.addAll(validNordVPN.get("uk{}.nordvpn.com")); dns.addAll(validNordVPN.get("us{}.nordvpn.com")); dns.addAll(validNordVPN.get("vn{}.nordvpn.com")); return getIPs(dns.toArray(new String[0]), 50); } private static LoadingCache<String, Set<String>> validNordVPN = Caffeine.newBuilder().build(ScoreCommand::findNordVPN); private static Set<String> findNordVPN(String dns) { if (ConfigUtil.getDebugOrFalse()) { logger.info("Building NordVPN set " + dns.replace("{}", "")); } Set<String> retVal = new HashSet<>(); for (int i = 0; i < 50; i++) { try { String name = dns.replace("{}", String.valueOf(i)); InetAddress.getByName(name); retVal.add(name); } catch (UnknownHostException ignored) { } } if (ConfigUtil.getDebugOrFalse()) { logger.info("Got " + retVal.size() + " value(s) for NordVPN set " + dns.replace("{}", "")); } return retVal; } private Set<String> getCryptostormIPs() { String[] dns = new String[] { "balancer.cstorm.is", "balancer.cstorm.net", "balancer.cryptostorm.ch", "balancer.cryptostorm.pw" }; return getIPs(dns, 50); } private static LoadingCache<String, Set<String>> records = Caffeine.newBuilder().build(ScoreCommand::collectRecords); private static Set<String> collectRecords(String dns) { if (ConfigUtil.getDebugOrFalse()) { logger.info("Collecting A records for " + dns); } Set<String> retVal = new HashSet<>(); try { InitialDirContext context = new InitialDirContext(); Attributes attributes = context.getAttributes("dns:/" + dns, new String[] { "A" }); NamingEnumeration<?> attributeEnum = attributes.get("A").getAll(); while (attributeEnum.hasMore()) { retVal.add(attributeEnum.next().toString()); } } catch (NamingException ex) { logger.error(ex.getMessage(), ex); } if (ConfigUtil.getDebugOrFalse()) { logger.info("Got " + retVal.size() + " record(s) for " + dns); } return retVal; } private Set<String> getHomeIPs() { String[] dns = new String[] { // Comcast - https://postmaster.comcast.net/dynamic-IP-ranges.html "24.0.0.0/12", "24.16.0.0/13", "24.30.0.0/17", "24.34.0.0/16", "24.60.0.0/14", "24.91.0.0/16", "24.98.0.0/15", "24.118.0.0/16", "24.125.0.0/16", "24.126.0.0/15", "24.128.0.0/16", "24.129.0.0/17", "24.130.0.0/15", "24.147.0.0/16", "24.218.0.0/16", "24.245.0.0/18", "50.128.0.0/10", "65.34.128.0/17", "65.96.0.0/16", "66.30.0.0/15", "66.41.0.0/16", "66.56.0.0/18", "66.176.0.0/15", "66.229.0.0/16", "67.160.0.0/12", "67.176.0.0/15", "67.180.0.0/14", "67.184.0.0/13", "68.32.0.0/11", "68.80.0.0/14", "68.84.0.0/16", "69.136.0.0/15", "69.138.0.0/16", "69.139.0.0/17", "69.140.0.0/14", "69.180.0.0/15", "69.242.0.0/15", "69.244.0.0/14", "69.248.0.0/14", "69.253.0.0/16", "69.254.0.0/15", "71.56.0.0/13", "71.192.0.0/12", "71.224.0.0/12", "73.0.0.0/8", "75.64.0.0/13", "75.72.0.0/15", "75.74.0.0/16", "75.75.0.0/17", "75.75.128.0/18", "76.16.0.0/12", "76.97.0.0/16", "76.98.0.0/15", "76.100.0.0/14", "76.104.0.0/13", "76.112.0.0/12", "98.192.0.0/13", "98.200.0.0/14", "98.204.0.0/16", "98.206.0.0/15", "98.208.0.0/12", "98.224.0.0/12", "98.240.0.0/16", "98.242.0.0/15", "98.244.0.0/14", "98.248.0.0/13", "107.2.0.0/15", "107.4.0.0/15", "174.48.0.0/12", "2001:558:6000::/36" }; return getIPs(dns, 50); } private Set<String> getIPs(String[] dns, int count) { Set<String> retVal = new HashSet<>(); int fails = 0; while (retVal.size() < count && fails < 1000) { String name; do { name = dns[(int) fairRoundedRandom(0L, (long) dns.length - 1L)]; } while (name == null); if (ValidationUtil.isValidIp(name)) { if (!retVal.add(name)) { fails++; } } else if (ValidationUtil.isValidIPRange(name)) { if (!retVal.addAll(getIPs(name, 1))) { fails++; } } else { List<String> r = new ArrayList<>(records.get(name)); if (r.isEmpty()) { continue; } if (!retVal.add(r.get((int) fairRoundedRandom(0L, (long) r.size() - 1L)))) { fails++; } } } return retVal; } private Set<String> getIPs(String mask, int count) { Set<String> retVal = new HashSet<>(); IPAddress range = new IPAddressString(mask).getAddress(); int fails = 0; while (retVal.size() < count && fails < 1000) { long getIndex = fairRoundedRandom(0L, range.getCount().longValue()); long i = 0; for (IPAddress ip : range.getIterable()) { if (i == getIndex) { String str = ip.toCanonicalString(); int idx = str.indexOf('/'); if (idx > -1) { str = str.substring(0, idx); } if (!retVal.add(str)) { fails++; } if (retVal.size() >= count || fails >= 1000) { break; } getIndex = fairRoundedRandom(0L, range.getCount().longValue()); if (getIndex <= i) { break; } } i++; } } return retVal; } private long fairRoundedRandom(long min, long max) { long num; max++; do { num = (long) Math.floor(Math.random() * (max - min) + min); } while (num > max - 1); return num; } }