PIA-ServerList/src/main/java/me/braydon/pia/PIAServerList.java

170 lines
6.8 KiB
Java
Raw Normal View History

2024-04-26 23:21:06 -04:00
package me.braydon.pia;
2024-04-27 00:56:22 -04:00
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
2024-04-26 23:21:06 -04:00
import lombok.NonNull;
2024-04-27 00:56:22 -04:00
import lombok.SneakyThrows;
import me.braydon.pia.readme.ReadMeManager;
import net.lingala.zip4j.ZipFile;
import org.xbill.DNS.Record;
2024-04-28 23:24:12 -04:00
import org.xbill.DNS.*;
2024-04-27 00:56:22 -04:00
import java.io.*;
2024-04-29 01:03:01 -04:00
import java.net.InetAddress;
2024-04-27 00:56:22 -04:00
import java.net.URL;
import java.nio.file.Files;
2024-04-28 23:24:12 -04:00
import java.util.*;
2024-04-27 00:56:22 -04:00
import java.util.concurrent.TimeUnit;
2024-04-26 23:21:06 -04:00
/**
* @author Braydon
*/
public final class PIAServerList {
2024-04-27 00:56:22 -04:00
private static final Gson GSON = new GsonBuilder()
.setPrettyPrinting()
.create();
private static final String OPENVPN_FILES_ENDPOINT = "https://www.privateinternetaccess.com/openvpn/openvpn.zip";
2024-04-27 18:48:43 -04:00
private static final File SERVERS_FILE = new File("servers.json");
2024-04-29 01:03:01 -04:00
private static final int TOTAL_RUNS = 3;
2024-04-27 00:56:22 -04:00
@SneakyThrows
2024-04-26 23:21:06 -04:00
public static void main(@NonNull String[] args) {
2024-04-28 23:24:12 -04:00
Map<String, String> regionAddresses = getRegionAddresses(); // Get region address from PIA
for (int i = 0; i < TOTAL_RUNS; i++) {
2024-04-29 01:27:21 -04:00
Set<PIAServer> servers = getNewServers(regionAddresses); // Get the new servers from PIA
2024-04-28 23:24:12 -04:00
int before = servers.size();
2024-04-29 01:27:21 -04:00
servers.addAll(loadServersFromFile()); // Load servers from the file
System.out.println("Loaded " + (servers.size() - before) + " server(s) from the servers file");
2024-04-28 23:24:12 -04:00
2024-04-29 01:27:21 -04:00
// Delete servers that haven't been seen in more than a week
before = servers.size();
servers.removeIf(server -> (System.currentTimeMillis() - server.getLastSeen()) >= TimeUnit.DAYS.toMillis(7L));
System.out.println("Removed " + (before - servers.size()) + " server(s) that haven't been seen in more than a week");
2024-04-28 23:24:12 -04:00
2024-04-29 01:27:21 -04:00
// Write the servers to the servers file
System.out.println("Writing servers file...");
try (FileWriter fileWriter = new FileWriter(SERVERS_FILE)) {
GSON.toJson(servers, fileWriter);
}
System.out.println("Done, wrote " + servers.size() + " servers to the file");
2024-04-27 00:56:22 -04:00
2024-04-29 01:27:21 -04:00
// Update the README.md file
ReadMeManager.update(servers);
2024-04-27 00:56:22 -04:00
2024-04-29 01:27:21 -04:00
// Sleep before running again
if (i < TOTAL_RUNS - 1) {
System.out.println("Sleeping...");
Thread.sleep(TimeUnit.MINUTES.toMillis(4L));
}
2024-04-27 00:56:22 -04:00
}
}
/**
* Get the new servers from the
* OpenVPN files provided by PIA.
*
2024-04-28 23:24:12 -04:00
* @param regionAddresses the region addresses
2024-04-27 00:56:22 -04:00
* @return the new servers
*/
2024-04-28 23:24:12 -04:00
@SneakyThrows @NonNull
private static Set<PIAServer> getNewServers(@NonNull Map<String, String> regionAddresses) {
2024-04-27 00:56:22 -04:00
Set<PIAServer> servers = new HashSet<>(); // The new servers to return
2024-04-28 23:24:12 -04:00
Lookup.setDefaultResolver(new SimpleResolver("1.1.1.1")); // Use CF DNS
for (Map.Entry<String, String> entry : regionAddresses.entrySet()) {
String region = entry.getKey();
String address = entry.getValue();
Record[] records = new Lookup(address, Type.A).run(); // Resolve A records
if (records == null) { // No A records resolved
continue;
}
System.out.println("Resolved " + records.length + " A Records for region " + region);
for (Record record : records) {
servers.add(new PIAServer(((ARecord) record).getAddress().getHostAddress(), region, System.currentTimeMillis()));
}
}
return servers;
}
/**
* Get the region addresses from
* the OpenVPN files provided by PIA.
*
* @return the mapped region addresses
*/
@SneakyThrows
private static Map<String, String> getRegionAddresses() {
Map<String, String> regionAddresses = new HashMap<>();
2024-04-27 00:56:22 -04:00
File serversZip = new File("servers.zip"); // The zip file containing the servers
// Download the OpenVPN servers zip from PIA
long before = System.currentTimeMillis();
System.out.println("Downloading OpenVPN files from PIA...");
try (InputStream inputStream = new URL(OPENVPN_FILES_ENDPOINT).openStream();
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
FileOutputStream fileOutputStream = new FileOutputStream(serversZip)
) {
byte[] dataBuffer = new byte[1024];
int bytesRead;
while ((bytesRead = bufferedInputStream.read(dataBuffer, 0, 1024)) != -1) {
fileOutputStream.write(dataBuffer, 0, bytesRead);
}
}
assert serversZip.exists(); // Confirm the zip exists
System.out.println("Downloaded in " + (System.currentTimeMillis() - before) + "ms, extracting...");
// Extract the servers zip files
before = System.currentTimeMillis();
try (ZipFile zip = new ZipFile(serversZip)) {
zip.extractAll("servers");
}
serversZip.delete(); // Delete the zip file after extraction
System.out.println("Extracted in " + (System.currentTimeMillis() - before) + "ms");
// Iterate over the OpenVPN files downloaded from PIA
File serversDir = new File("servers"); // The dir where the downloaded OpenVPN files are stored
File[] openVpnFiles = serversDir.listFiles();
assert openVpnFiles != null;
System.out.println("Found " + openVpnFiles.length + " OpenVPN files, reading them...");
for (File file : openVpnFiles) {
String region = file.getName().split("\\.")[0]; // The server region
try {
for (String line : Files.readAllLines(file.toPath())) {
// Line doesn't contain the remote server, ignore it
if (!line.startsWith("remote ")) {
continue;
}
2024-04-28 23:24:12 -04:00
// Store the region -> address mapping
regionAddresses.put(region, line.split(" ")[1]);
2024-04-27 00:56:22 -04:00
}
} finally {
file.delete(); // Delete the OpenVPN file after reading it
}
}
serversDir.delete(); // Delete the servers dir after reading the OpenVPN files
2024-04-28 23:24:12 -04:00
return regionAddresses;
2024-04-27 00:56:22 -04:00
}
/**
2024-04-27 18:48:43 -04:00
* Load the servers from the json file.
2024-04-27 00:56:22 -04:00
*
* @return the loaded servers
*/
@SneakyThrows
2024-04-27 18:48:43 -04:00
private static List<PIAServer> loadServersFromFile() {
if (!SERVERS_FILE.exists()) { // No servers file to load
2024-04-27 00:56:22 -04:00
return new ArrayList<>();
}
2024-04-27 18:48:43 -04:00
try (FileReader fileReader = new FileReader(SERVERS_FILE);
2024-04-27 00:56:22 -04:00
JsonReader jsonReader = new JsonReader(fileReader)
) {
return GSON.fromJson(jsonReader, new TypeToken<List<PIAServer>>() {}.getType());
}
2024-04-26 23:21:06 -04:00
}
}