global: format code
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
@@ -9,6 +9,21 @@ import com.wireguard.util.NonNullForAll;
|
||||
|
||||
@NonNullForAll
|
||||
public final class BackendException extends Exception {
|
||||
private final Object[] format;
|
||||
private final Reason reason;
|
||||
public BackendException(final Reason reason, final Object... format) {
|
||||
this.reason = reason;
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public Object[] getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public Reason getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public enum Reason {
|
||||
UNKNOWN_KERNEL_MODULE_NAME,
|
||||
WG_QUICK_CONFIG_ERROR_CODE,
|
||||
@@ -18,16 +33,4 @@ public final class BackendException extends Exception {
|
||||
TUN_CREATION_ERROR,
|
||||
GO_ACTIVATION_ERROR_CODE
|
||||
}
|
||||
private final Reason reason;
|
||||
private final Object[] format;
|
||||
public BackendException(final Reason reason, final Object ...format) {
|
||||
this.reason = reason;
|
||||
this.format = format;
|
||||
}
|
||||
public Reason getReason() {
|
||||
return reason;
|
||||
}
|
||||
public Object[] getFormat() {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,25 +34,21 @@ import java9.util.concurrent.CompletableFuture;
|
||||
@NonNullForAll
|
||||
public final class GoBackend implements Backend {
|
||||
private static final String TAG = "WireGuard/" + GoBackend.class.getSimpleName();
|
||||
private static CompletableFuture<VpnService> vpnService = new CompletableFuture<>();
|
||||
public interface AlwaysOnCallback {
|
||||
void alwaysOnTriggered();
|
||||
}
|
||||
@Nullable private static AlwaysOnCallback alwaysOnCallback;
|
||||
public static void setAlwaysOnCallback(AlwaysOnCallback cb) {
|
||||
alwaysOnCallback = cb;
|
||||
}
|
||||
|
||||
private static CompletableFuture<VpnService> vpnService = new CompletableFuture<>();
|
||||
private final Context context;
|
||||
@Nullable private Tunnel currentTunnel;
|
||||
@Nullable private Config currentConfig;
|
||||
@Nullable private Tunnel currentTunnel;
|
||||
private int currentTunnelHandle = -1;
|
||||
|
||||
public GoBackend(final Context context) {
|
||||
SharedLibraryLoader.loadSharedLibrary(context, "wg-go");
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public static void setAlwaysOnCallback(AlwaysOnCallback cb) {
|
||||
alwaysOnCallback = cb;
|
||||
}
|
||||
|
||||
private static native String wgGetConfig(int handle);
|
||||
|
||||
private static native int wgGetSocketV4(int handle);
|
||||
@@ -143,7 +139,7 @@ public final class GoBackend implements Backend {
|
||||
setStateInternal(currentTunnel, null, State.DOWN);
|
||||
try {
|
||||
setStateInternal(tunnel, config, state);
|
||||
} catch(final Exception e) {
|
||||
} catch (final Exception e) {
|
||||
if (originalTunnel != null)
|
||||
setStateInternal(originalTunnel, originalConfig, State.UP);
|
||||
throw e;
|
||||
@@ -209,7 +205,7 @@ public final class GoBackend implements Backend {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
builder.setMetered(false);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
service.setUnderlyingNetworks(null);
|
||||
service.setUnderlyingNetworks(null);
|
||||
|
||||
builder.setBlocking(true);
|
||||
try (final ParcelFileDescriptor tun = builder.establish()) {
|
||||
@@ -246,13 +242,13 @@ public final class GoBackend implements Backend {
|
||||
context.startService(new Intent(context, VpnService.class));
|
||||
}
|
||||
|
||||
public interface AlwaysOnCallback {
|
||||
void alwaysOnTriggered();
|
||||
}
|
||||
|
||||
public static class VpnService extends android.net.VpnService {
|
||||
@Nullable private GoBackend owner;
|
||||
|
||||
public void setOwner(final GoBackend owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public Builder getBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
@@ -290,5 +286,9 @@ public final class GoBackend implements Backend {
|
||||
}
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
public void setOwner(final GoBackend owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,11 @@ import java.util.Map;
|
||||
|
||||
@NonNullForAll
|
||||
public class Statistics {
|
||||
private long lastTouched = SystemClock.elapsedRealtime();
|
||||
private final Map<Key, Pair<Long, Long>> peerBytes = new HashMap<>();
|
||||
private long lastTouched = SystemClock.elapsedRealtime();
|
||||
|
||||
Statistics() { }
|
||||
Statistics() {
|
||||
}
|
||||
|
||||
void add(final Key key, final long rx, final long tx) {
|
||||
peerBytes.put(key, Pair.create(rx, tx));
|
||||
@@ -30,10 +31,6 @@ public class Statistics {
|
||||
return SystemClock.elapsedRealtime() - lastTouched > 900;
|
||||
}
|
||||
|
||||
public Key[] peers() {
|
||||
return peerBytes.keySet().toArray(new Key[0]);
|
||||
}
|
||||
|
||||
public long peerRx(final Key peer) {
|
||||
if (!peerBytes.containsKey(peer))
|
||||
return 0;
|
||||
@@ -46,6 +43,10 @@ public class Statistics {
|
||||
return peerBytes.get(peer).second;
|
||||
}
|
||||
|
||||
public Key[] peers() {
|
||||
return peerBytes.keySet().toArray(new Key[0]);
|
||||
}
|
||||
|
||||
public long totalRx() {
|
||||
long rx = 0;
|
||||
for (final Pair<Long, Long> val : peerBytes.values()) {
|
||||
|
||||
@@ -15,16 +15,6 @@ import java.util.regex.Pattern;
|
||||
|
||||
@NonNullForAll
|
||||
public interface Tunnel {
|
||||
enum State {
|
||||
DOWN,
|
||||
TOGGLE,
|
||||
UP;
|
||||
|
||||
public static State of(final boolean running) {
|
||||
return running ? UP : DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
int NAME_MAX_LENGTH = 15;
|
||||
Pattern NAME_PATTERN = Pattern.compile("[a-zA-Z0-9_=+.-]{1,15}");
|
||||
|
||||
@@ -46,4 +36,14 @@ public interface Tunnel {
|
||||
* @return The new state of the tunnel.
|
||||
*/
|
||||
void onStateChange(State newState);
|
||||
|
||||
enum State {
|
||||
DOWN,
|
||||
TOGGLE,
|
||||
UP;
|
||||
|
||||
public static State of(final boolean running) {
|
||||
return running ? UP : DOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,11 +42,10 @@ import java9.util.stream.Stream;
|
||||
@NonNullForAll
|
||||
public final class WgQuickBackend implements Backend {
|
||||
private static final String TAG = "WireGuard/" + WgQuickBackend.class.getSimpleName();
|
||||
|
||||
private final RootShell rootShell;
|
||||
private final ToolsInstaller toolsInstaller;
|
||||
private final File localTemporaryDir;
|
||||
private final RootShell rootShell;
|
||||
private final Map<Tunnel, Config> runningConfigs = new HashMap<>();
|
||||
private final ToolsInstaller toolsInstaller;
|
||||
private boolean multipleTunnels;
|
||||
|
||||
public WgQuickBackend(final Context context, final RootShell rootShell, final ToolsInstaller toolsInstaller) {
|
||||
@@ -55,10 +54,6 @@ public final class WgQuickBackend implements Backend {
|
||||
this.toolsInstaller = toolsInstaller;
|
||||
}
|
||||
|
||||
public void setMultipleTunnels(boolean on) {
|
||||
multipleTunnels = on;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRunningTunnelNames() {
|
||||
final List<String> output = new ArrayList<>();
|
||||
@@ -110,6 +105,10 @@ public final class WgQuickBackend implements Backend {
|
||||
return output.get(0);
|
||||
}
|
||||
|
||||
public void setMultipleTunnels(boolean on) {
|
||||
multipleTunnels = on;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State setState(final Tunnel tunnel, State state, @Nullable final Config config) throws Exception {
|
||||
final State originalState = getState(tunnel);
|
||||
@@ -135,7 +134,8 @@ public final class WgQuickBackend implements Backend {
|
||||
for (final Pair<Tunnel, Config> entry : rewind) {
|
||||
setStateInternal(entry.first, entry.second, State.UP);
|
||||
}
|
||||
} catch (final Exception ignored) { }
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@@ -153,7 +153,8 @@ public final class WgQuickBackend implements Backend {
|
||||
setStateInternal(entry.getKey(), entry.getValue(), State.UP);
|
||||
}
|
||||
}
|
||||
} catch (final Exception ignored) { }
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
} else if (state == State.DOWN) {
|
||||
|
||||
@@ -39,15 +39,14 @@ import androidx.annotation.Nullable;
|
||||
|
||||
@NonNullForAll
|
||||
public class ModuleLoader {
|
||||
private static final String MODULE_PUBLIC_KEY_BASE64 = "RWRmHuT9PSqtwfsLtEx+QS06BJtLgFYteL9WCNjH7yuyu5Y1DieSN7If";
|
||||
private static final String MODULE_LIST_URL = "https://download.wireguard.com/android-module/modules.txt.sig";
|
||||
private static final String MODULE_URL = "https://download.wireguard.com/android-module/%s";
|
||||
private static final String MODULE_NAME = "wireguard-%s.ko";
|
||||
|
||||
private final RootShell rootShell;
|
||||
private final String userAgent;
|
||||
private static final String MODULE_PUBLIC_KEY_BASE64 = "RWRmHuT9PSqtwfsLtEx+QS06BJtLgFYteL9WCNjH7yuyu5Y1DieSN7If";
|
||||
private static final String MODULE_URL = "https://download.wireguard.com/android-module/%s";
|
||||
private final File moduleDir;
|
||||
private final RootShell rootShell;
|
||||
private final File tmpDir;
|
||||
private final String userAgent;
|
||||
|
||||
public ModuleLoader(final Context context, final RootShell rootShell, final String userAgent) {
|
||||
moduleDir = new File(context.getCacheDir(), "kmod");
|
||||
@@ -56,27 +55,75 @@ public class ModuleLoader {
|
||||
this.userAgent = userAgent;
|
||||
}
|
||||
|
||||
public boolean moduleMightExist() {
|
||||
return moduleDir.exists() && moduleDir.isDirectory();
|
||||
public static boolean isModuleLoaded() {
|
||||
return new File("/sys/module/wireguard").exists();
|
||||
}
|
||||
|
||||
public Integer download() throws IOException, RootShellException, NoSuchAlgorithmException {
|
||||
final List<String> output = new ArrayList<>();
|
||||
rootShell.run(output, "sha256sum /proc/version|cut -d ' ' -f 1");
|
||||
if (output.size() != 1 || output.get(0).length() != 64)
|
||||
throw new InvalidParameterException("Invalid sha256 of /proc/version");
|
||||
final String moduleName = String.format(MODULE_NAME, output.get(0));
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(MODULE_LIST_URL).openConnection();
|
||||
connection.setRequestProperty("User-Agent", userAgent);
|
||||
connection.connect();
|
||||
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
|
||||
throw new IOException("Hash list could not be found");
|
||||
byte[] input = new byte[1024 * 1024 * 3 /* 3MiB */];
|
||||
int len;
|
||||
try (final InputStream inputStream = connection.getInputStream()) {
|
||||
len = inputStream.read(input);
|
||||
}
|
||||
if (len <= 0)
|
||||
throw new IOException("Hash list was empty");
|
||||
final Map<String, Sha256Digest> modules = verifySignedHashes(new String(input, 0, len, StandardCharsets.UTF_8));
|
||||
if (modules == null)
|
||||
throw new InvalidParameterException("The signature did not verify or invalid hash list format");
|
||||
if (!modules.containsKey(moduleName))
|
||||
return OsConstants.ENOENT;
|
||||
connection = (HttpURLConnection) new URL(String.format(MODULE_URL, moduleName)).openConnection();
|
||||
connection.setRequestProperty("User-Agent", userAgent);
|
||||
connection.connect();
|
||||
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
|
||||
throw new IOException("Module file could not be found, despite being on hash list");
|
||||
|
||||
tmpDir.mkdirs();
|
||||
moduleDir.mkdir();
|
||||
File tempFile = null;
|
||||
try {
|
||||
tempFile = File.createTempFile("UNVERIFIED-", null, tmpDir);
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
try (final InputStream inputStream = connection.getInputStream();
|
||||
final FileOutputStream outputStream = new FileOutputStream(tempFile)) {
|
||||
int total = 0;
|
||||
while ((len = inputStream.read(input)) > 0) {
|
||||
total += len;
|
||||
if (total > 1024 * 1024 * 15 /* 15 MiB */)
|
||||
throw new IOException("File too big");
|
||||
outputStream.write(input, 0, len);
|
||||
digest.update(input, 0, len);
|
||||
}
|
||||
outputStream.getFD().sync();
|
||||
}
|
||||
if (!Arrays.equals(digest.digest(), modules.get(moduleName).bytes))
|
||||
throw new IOException("Incorrect file hash");
|
||||
|
||||
if (!tempFile.renameTo(new File(moduleDir, moduleName)))
|
||||
throw new IOException("Unable to rename to final destination");
|
||||
} finally {
|
||||
if (tempFile != null)
|
||||
tempFile.delete();
|
||||
}
|
||||
return OsConstants.EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
public void loadModule() throws IOException, RootShellException {
|
||||
rootShell.run(null, String.format("insmod \"%s/wireguard-$(sha256sum /proc/version|cut -d ' ' -f 1).ko\"", moduleDir.getAbsolutePath()));
|
||||
}
|
||||
|
||||
public static boolean isModuleLoaded() {
|
||||
return new File("/sys/module/wireguard").exists();
|
||||
}
|
||||
|
||||
private static final class Sha256Digest {
|
||||
private byte[] bytes;
|
||||
private Sha256Digest(final String hex) {
|
||||
if (hex.length() != 64)
|
||||
throw new InvalidParameterException("SHA256 hashes must be 32 bytes long");
|
||||
bytes = new byte[32];
|
||||
for (int i = 0; i < 32; ++i)
|
||||
bytes[i] = (byte)Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
|
||||
}
|
||||
public boolean moduleMightExist() {
|
||||
return moduleDir.exists() && moduleDir.isDirectory();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -127,62 +174,15 @@ public class ModuleLoader {
|
||||
return hashes;
|
||||
}
|
||||
|
||||
public Integer download() throws IOException, RootShellException, NoSuchAlgorithmException {
|
||||
final List<String> output = new ArrayList<>();
|
||||
rootShell.run(output, "sha256sum /proc/version|cut -d ' ' -f 1");
|
||||
if (output.size() != 1 || output.get(0).length() != 64)
|
||||
throw new InvalidParameterException("Invalid sha256 of /proc/version");
|
||||
final String moduleName = String.format(MODULE_NAME, output.get(0));
|
||||
HttpURLConnection connection = (HttpURLConnection)new URL(MODULE_LIST_URL).openConnection();
|
||||
connection.setRequestProperty("User-Agent", userAgent);
|
||||
connection.connect();
|
||||
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
|
||||
throw new IOException("Hash list could not be found");
|
||||
byte[] input = new byte[1024 * 1024 * 3 /* 3MiB */];
|
||||
int len;
|
||||
try (final InputStream inputStream = connection.getInputStream()) {
|
||||
len = inputStream.read(input);
|
||||
}
|
||||
if (len <= 0)
|
||||
throw new IOException("Hash list was empty");
|
||||
final Map<String, Sha256Digest> modules = verifySignedHashes(new String(input, 0, len, StandardCharsets.UTF_8));
|
||||
if (modules == null)
|
||||
throw new InvalidParameterException("The signature did not verify or invalid hash list format");
|
||||
if (!modules.containsKey(moduleName))
|
||||
return OsConstants.ENOENT;
|
||||
connection = (HttpURLConnection)new URL(String.format(MODULE_URL, moduleName)).openConnection();
|
||||
connection.setRequestProperty("User-Agent", userAgent);
|
||||
connection.connect();
|
||||
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
|
||||
throw new IOException("Module file could not be found, despite being on hash list");
|
||||
private static final class Sha256Digest {
|
||||
private byte[] bytes;
|
||||
|
||||
tmpDir.mkdirs();
|
||||
moduleDir.mkdir();
|
||||
File tempFile = null;
|
||||
try {
|
||||
tempFile = File.createTempFile("UNVERIFIED-", null, tmpDir);
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
try (final InputStream inputStream = connection.getInputStream();
|
||||
final FileOutputStream outputStream = new FileOutputStream(tempFile)) {
|
||||
int total = 0;
|
||||
while ((len = inputStream.read(input)) > 0) {
|
||||
total += len;
|
||||
if (total > 1024 * 1024 * 15 /* 15 MiB */)
|
||||
throw new IOException("File too big");
|
||||
outputStream.write(input, 0, len);
|
||||
digest.update(input, 0, len);
|
||||
}
|
||||
outputStream.getFD().sync();
|
||||
}
|
||||
if (!Arrays.equals(digest.digest(), modules.get(moduleName).bytes))
|
||||
throw new IOException("Incorrect file hash");
|
||||
|
||||
if (!tempFile.renameTo(new File(moduleDir, moduleName)))
|
||||
throw new IOException("Unable to rename to final destination");
|
||||
} finally {
|
||||
if (tempFile != null)
|
||||
tempFile.delete();
|
||||
private Sha256Digest(final String hex) {
|
||||
if (hex.length() != 64)
|
||||
throw new InvalidParameterException("SHA256 hashes must be 32 bytes long");
|
||||
bytes = new byte[32];
|
||||
for (int i = 0; i < 32; ++i)
|
||||
bytes[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
|
||||
}
|
||||
return OsConstants.EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,6 +187,25 @@ public class RootShell {
|
||||
}
|
||||
|
||||
public static class RootShellException extends Exception {
|
||||
private final Object[] format;
|
||||
private final Reason reason;
|
||||
public RootShellException(final Reason reason, final Object... format) {
|
||||
this.reason = reason;
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public Object[] getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public Reason getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public boolean isIORelated() {
|
||||
return reason != Reason.NO_ROOT_ACCESS;
|
||||
}
|
||||
|
||||
public enum Reason {
|
||||
NO_ROOT_ACCESS,
|
||||
SHELL_MARKER_COUNT_ERROR,
|
||||
@@ -195,20 +214,5 @@ public class RootShell {
|
||||
CREATE_BIN_DIR_ERROR,
|
||||
CREATE_TEMP_DIR_ERROR
|
||||
}
|
||||
private final Reason reason;
|
||||
private final Object[] format;
|
||||
public RootShellException(final Reason reason, final Object ...format) {
|
||||
this.reason = reason;
|
||||
this.format = format;
|
||||
}
|
||||
public boolean isIORelated() {
|
||||
return reason != Reason.NO_ROOT_ACCESS;
|
||||
}
|
||||
public Reason getReason() {
|
||||
return reason;
|
||||
}
|
||||
public Object[] getFormat() {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,9 +40,9 @@ public final class ToolsInstaller {
|
||||
private static final String TAG = "WireGuard/" + ToolsInstaller.class.getSimpleName();
|
||||
|
||||
private final Context context;
|
||||
private final RootShell rootShell;
|
||||
private final File localBinaryDir;
|
||||
private final Object lock = new Object();
|
||||
private final RootShell rootShell;
|
||||
@Nullable private Boolean areToolsAvailable;
|
||||
@Nullable private Boolean installAsMagiskModule;
|
||||
|
||||
@@ -107,6 +107,29 @@ public final class ToolsInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean extract() throws IOException {
|
||||
localBinaryDir.mkdirs();
|
||||
final File files[] = new File[EXECUTABLES.length];
|
||||
final File tempFiles[] = new File[EXECUTABLES.length];
|
||||
boolean allExist = true;
|
||||
for (int i = 0; i < files.length; ++i) {
|
||||
files[i] = new File(localBinaryDir, EXECUTABLES[i]);
|
||||
tempFiles[i] = new File(localBinaryDir, EXECUTABLES[i] + ".tmp");
|
||||
allExist &= files[i].exists();
|
||||
}
|
||||
if (allExist)
|
||||
return false;
|
||||
for (int i = 0; i < files.length; ++i) {
|
||||
if (!SharedLibraryLoader.extractLibrary(context, EXECUTABLES[i], tempFiles[i]))
|
||||
throw new FileNotFoundException("Unable to find " + EXECUTABLES[i]);
|
||||
if (!tempFiles[i].setExecutable(true, false))
|
||||
throw new IOException("Unable to mark " + tempFiles[i].getAbsolutePath() + " as executable");
|
||||
if (!tempFiles[i].renameTo(files[i]))
|
||||
throw new IOException("Unable to rename " + tempFiles[i].getAbsolutePath() + " to " + files[i].getAbsolutePath());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int install() throws RootShellException, IOException {
|
||||
if (!context.getPackageName().startsWith("com.wireguard."))
|
||||
throw new SecurityException("The tools may only be installed system-wide from the main WireGuard app.");
|
||||
@@ -161,29 +184,6 @@ public final class ToolsInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean extract() throws IOException {
|
||||
localBinaryDir.mkdirs();
|
||||
final File files[] = new File[EXECUTABLES.length];
|
||||
final File tempFiles[] = new File[EXECUTABLES.length];
|
||||
boolean allExist = true;
|
||||
for (int i = 0; i < files.length; ++i) {
|
||||
files[i] = new File(localBinaryDir, EXECUTABLES[i]);
|
||||
tempFiles[i] = new File(localBinaryDir, EXECUTABLES[i] + ".tmp");
|
||||
allExist &= files[i].exists();
|
||||
}
|
||||
if (allExist)
|
||||
return false;
|
||||
for (int i = 0; i < files.length; ++i) {
|
||||
if (!SharedLibraryLoader.extractLibrary(context, EXECUTABLES[i], tempFiles[i]))
|
||||
throw new FileNotFoundException("Unable to find " + EXECUTABLES[i]);
|
||||
if (!tempFiles[i].setExecutable(true, false))
|
||||
throw new IOException("Unable to mark " + tempFiles[i].getAbsolutePath() + " as executable");
|
||||
if (!tempFiles[i].renameTo(files[i]))
|
||||
throw new IOException("Unable to rename " + tempFiles[i].getAbsolutePath() + " to " + files[i].getAbsolutePath());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean willInstallAsMagiskModule() {
|
||||
synchronized (lock) {
|
||||
if (installAsMagiskModule == null) {
|
||||
|
||||
@@ -35,7 +35,8 @@ public final class InetAddresses {
|
||||
PARSER_METHOD = m;
|
||||
}
|
||||
|
||||
private InetAddresses() { }
|
||||
private InetAddresses() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a numeric IPv4 or IPv6 address without performing any DNS lookups.
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.wireguard.util.NonNullForAll;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@NonNullForAll
|
||||
public class ParseException extends Exception {
|
||||
|
||||
@@ -204,6 +204,16 @@ public final class Key {
|
||||
return new Key(publicKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj == this)
|
||||
return true;
|
||||
if (obj == null || obj.getClass() != getClass())
|
||||
return false;
|
||||
final Key other = (Key) obj;
|
||||
return MessageDigest.isEqual(key, other.key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key as an array of bytes.
|
||||
*
|
||||
@@ -214,6 +224,14 @@ public final class Key {
|
||||
return Arrays.copyOf(key, key.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int ret = 0;
|
||||
for (int i = 0; i < key.length / 4; ++i)
|
||||
ret ^= (key[i * 4 + 0] >> 0) + (key[i * 4 + 1] >> 8) + (key[i * 4 + 2] >> 16) + (key[i * 4 + 3] >> 24);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the key to base64.
|
||||
*
|
||||
@@ -250,24 +268,6 @@ public final class Key {
|
||||
return new String(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int ret = 0;
|
||||
for (int i = 0; i < key.length / 4; ++i)
|
||||
ret ^= (key[i * 4 + 0] >> 0) + (key[i * 4 + 1] >> 8) + (key[i * 4 + 2] >> 16) + (key[i * 4 + 3] >> 24);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj == this)
|
||||
return true;
|
||||
if (obj == null || obj.getClass() != getClass())
|
||||
return false;
|
||||
final Key other = (Key) obj;
|
||||
return MessageDigest.isEqual(key, other.key);
|
||||
}
|
||||
|
||||
/**
|
||||
* The supported formats for encoding a WireGuard key.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user