mirror of
https://github.com/amnezia-vpn/amneziawg-android.git
synced 2026-07-03 14:05:13 +02:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b67fa3a38c | |||
| 8b0123042f | |||
| 16890a659e | |||
| d40ac7f89d | |||
| 2694f48b87 | |||
| 0f91aeb2d3 | |||
| bc0111f895 | |||
| f8a3e9b332 | |||
| db9397fd3e | |||
| 20717ff128 | |||
| a532a88585 | |||
| 0b077bd523 | |||
| e008efcf97 | |||
| 7cf676f9bd |
+2
-2
@@ -17,8 +17,8 @@ android {
|
||||
applicationId 'com.wireguard.android'
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 455
|
||||
versionName '0.0.20191013'
|
||||
versionCode 459
|
||||
versionName '0.0.20191120'
|
||||
buildConfigField 'int', 'MIN_SDK_VERSION', "$minSdkVersion.apiLevel"
|
||||
}
|
||||
// If the keystore file exists
|
||||
|
||||
@@ -12,6 +12,8 @@ import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
@@ -26,12 +28,22 @@ import com.wireguard.android.util.ModuleLoader;
|
||||
import com.wireguard.android.util.RootShell;
|
||||
import com.wireguard.android.util.ToolsInstaller;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Locale;
|
||||
|
||||
import java9.util.concurrent.CompletableFuture;
|
||||
|
||||
public class Application extends android.app.Application {
|
||||
private static final String TAG = "WireGuard/" + Application.class.getSimpleName();
|
||||
public static final String USER_AGENT;
|
||||
|
||||
static {
|
||||
String preferredAbi = "unknown ABI";
|
||||
if (Build.SUPPORTED_ABIS.length > 0)
|
||||
preferredAbi = Build.SUPPORTED_ABIS[0];
|
||||
USER_AGENT = String.format(Locale.ENGLISH, "WireGuard/%s (Android %d; %s; %s; %s %s; %s)", BuildConfig.VERSION_NAME, Build.VERSION.SDK_INT, preferredAbi, Build.BOARD, Build.MANUFACTURER, Build.MODEL, Build.FINGERPRINT);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems") private static WeakReference<Application> weakSelf;
|
||||
private final CompletableFuture<Backend> futureBackend = new CompletableFuture<>();
|
||||
@SuppressWarnings("NullableProblems") private AsyncWorker asyncWorker;
|
||||
@@ -123,6 +135,7 @@ public class Application extends android.app.Application {
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
Log.i(TAG, USER_AGENT);
|
||||
super.onCreate();
|
||||
|
||||
asyncWorker = new AsyncWorker(AsyncTask.SERIAL_EXECUTOR, new Handler(Looper.getMainLooper()));
|
||||
|
||||
@@ -24,6 +24,8 @@ import com.wireguard.android.util.SharedLibraryLoader;
|
||||
import com.wireguard.config.Config;
|
||||
import com.wireguard.config.InetNetwork;
|
||||
import com.wireguard.config.Peer;
|
||||
import com.wireguard.crypto.Key;
|
||||
import com.wireguard.crypto.KeyFormatException;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.Collections;
|
||||
@@ -47,6 +49,8 @@ public final class GoBackend implements Backend {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
private static native String wgGetConfig(int handle);
|
||||
|
||||
private static native int wgGetSocketV4(int handle);
|
||||
|
||||
private static native int wgGetSocketV6(int handle);
|
||||
@@ -90,7 +94,45 @@ public final class GoBackend implements Backend {
|
||||
|
||||
@Override
|
||||
public Statistics getStatistics(final Tunnel tunnel) {
|
||||
return new Statistics();
|
||||
final Statistics stats = new Statistics();
|
||||
if (tunnel != currentTunnel) {
|
||||
return stats;
|
||||
}
|
||||
final String config = wgGetConfig(currentTunnelHandle);
|
||||
Key key = null;
|
||||
long rx = 0, tx = 0;
|
||||
for (final String line : config.split("\\n")) {
|
||||
if (line.startsWith("public_key=")) {
|
||||
if (key != null)
|
||||
stats.add(key, rx, tx);
|
||||
rx = 0;
|
||||
tx = 0;
|
||||
try {
|
||||
key = Key.fromHex(line.substring(11));
|
||||
} catch (final KeyFormatException ignored) {
|
||||
key = null;
|
||||
}
|
||||
} else if (line.startsWith("rx_bytes=")) {
|
||||
if (key == null)
|
||||
continue;
|
||||
try {
|
||||
rx = Long.parseLong(line.substring(9));
|
||||
} catch (final NumberFormatException ignored) {
|
||||
rx = 0;
|
||||
}
|
||||
} else if (line.startsWith("tx_bytes=")) {
|
||||
if (key == null)
|
||||
continue;
|
||||
try {
|
||||
tx = Long.parseLong(line.substring(9));
|
||||
} catch (final NumberFormatException ignored) {
|
||||
tx = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key != null)
|
||||
stats.add(key, rx, tx);
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,11 +15,13 @@ import com.wireguard.android.model.Tunnel;
|
||||
import com.wireguard.android.model.Tunnel.State;
|
||||
import com.wireguard.android.model.Tunnel.Statistics;
|
||||
import com.wireguard.config.Config;
|
||||
import com.wireguard.crypto.Key;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -83,7 +85,24 @@ public final class WgQuickBackend implements Backend {
|
||||
|
||||
@Override
|
||||
public Statistics getStatistics(final Tunnel tunnel) {
|
||||
return new Statistics();
|
||||
final Statistics stats = new Statistics();
|
||||
final Collection<String> output = new ArrayList<>();
|
||||
try {
|
||||
if (Application.getRootShell().run(output, String.format("wg show '%s' transfer", tunnel.getName())) != 0)
|
||||
return stats;
|
||||
} catch (final Exception ignored) {
|
||||
return stats;
|
||||
}
|
||||
for (final String line : output) {
|
||||
final String[] parts = line.split("\\t");
|
||||
if (parts.length != 3)
|
||||
continue;
|
||||
try {
|
||||
stats.add(Key.fromBase64(parts[0]), Long.parseLong(parts[1]), Long.parseLong(parts[2]));
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -7,6 +7,8 @@ package com.wireguard.android.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
@@ -15,7 +17,13 @@ import android.view.ViewGroup;
|
||||
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.databinding.TunnelDetailFragmentBinding;
|
||||
import com.wireguard.android.databinding.TunnelDetailPeerBinding;
|
||||
import com.wireguard.android.model.Tunnel;
|
||||
import com.wireguard.android.model.Tunnel.State;
|
||||
import com.wireguard.crypto.Key;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* Fragment that shows details about a specific tunnel.
|
||||
@@ -23,6 +31,20 @@ import com.wireguard.android.model.Tunnel;
|
||||
|
||||
public class TunnelDetailFragment extends BaseFragment {
|
||||
@Nullable private TunnelDetailFragmentBinding binding;
|
||||
@Nullable private Timer timer;
|
||||
@Nullable private State lastState = State.TOGGLE;
|
||||
|
||||
private static class StatsTimerTask extends TimerTask {
|
||||
final TunnelDetailFragment tdf;
|
||||
private StatsTimerTask(final TunnelDetailFragment tdf) {
|
||||
this.tdf = tdf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
tdf.updateStats();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
@@ -35,6 +57,22 @@ public class TunnelDetailFragment extends BaseFragment {
|
||||
inflater.inflate(R.menu.tunnel_detail, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
timer = new Timer();
|
||||
timer.scheduleAtFixedRate(new StatsTimerTask(this), 0, 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
@@ -59,6 +97,8 @@ public class TunnelDetailFragment extends BaseFragment {
|
||||
binding.setConfig(null);
|
||||
else
|
||||
newTunnel.getConfigAsync().thenAccept(binding::setConfig);
|
||||
lastState = State.TOGGLE;
|
||||
updateStats();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,4 +112,52 @@ public class TunnelDetailFragment extends BaseFragment {
|
||||
super.onViewStateRestored(savedInstanceState);
|
||||
}
|
||||
|
||||
private String formatBytes(final long bytes) {
|
||||
if (bytes < 1024)
|
||||
return getContext().getString(R.string.transfer_bytes, bytes);
|
||||
else if (bytes < 1024*1024)
|
||||
return getContext().getString(R.string.transfer_kibibytes, bytes/1024.0);
|
||||
else if (bytes < 1024*1024*1024)
|
||||
return getContext().getString(R.string.transfer_mibibytes, bytes/(1024.0*1024.0));
|
||||
else if (bytes < 1024*1024*1024*1024)
|
||||
return getContext().getString(R.string.transfer_gibibytes, bytes/(1024.0*1024.0*1024.0));
|
||||
return getContext().getString(R.string.transfer_tibibytes, bytes/(1024.0*1024.0*1024.0)/1024.0);
|
||||
}
|
||||
|
||||
private void updateStats() {
|
||||
if (binding == null || !isResumed())
|
||||
return;
|
||||
final State state = binding.getTunnel().getState();
|
||||
if (state != State.UP && lastState == state)
|
||||
return;
|
||||
lastState = state;
|
||||
binding.getTunnel().getStatisticsAsync().whenComplete((statistics, throwable) -> {
|
||||
if (throwable != null) {
|
||||
for (int i = 0; i < binding.peersLayout.getChildCount(); ++i) {
|
||||
final TunnelDetailPeerBinding peer = DataBindingUtil.getBinding(binding.peersLayout.getChildAt(i));
|
||||
if (peer == null)
|
||||
continue;
|
||||
peer.transferLabel.setVisibility(View.GONE);
|
||||
peer.transferText.setVisibility(View.GONE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < binding.peersLayout.getChildCount(); ++i) {
|
||||
final TunnelDetailPeerBinding peer = DataBindingUtil.getBinding(binding.peersLayout.getChildAt(i));
|
||||
if (peer == null)
|
||||
continue;
|
||||
final Key publicKey = peer.getItem().getPublicKey();
|
||||
final long rx = statistics.peerRx(publicKey);
|
||||
final long tx = statistics.peerTx(publicKey);
|
||||
if (rx == 0 && tx == 0) {
|
||||
peer.transferLabel.setVisibility(View.GONE);
|
||||
peer.transferText.setVisibility(View.GONE);
|
||||
continue;
|
||||
}
|
||||
peer.transferText.setText(getContext().getString(R.string.transfer_rx_tx, formatBytes(rx), formatBytes(tx)));
|
||||
peer.transferLabel.setVisibility(View.VISIBLE);
|
||||
peer.transferText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
|
||||
package com.wireguard.android.model;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.databinding.BaseObservable;
|
||||
import androidx.databinding.Bindable;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -12,8 +15,11 @@ import androidx.annotation.Nullable;
|
||||
import com.wireguard.android.BR;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.config.Config;
|
||||
import com.wireguard.crypto.Key;
|
||||
import com.wireguard.util.Keyed;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import java9.util.concurrent.CompletableFuture;
|
||||
@@ -85,15 +91,13 @@ public class Tunnel extends BaseObservable implements Keyed<String> {
|
||||
@Bindable
|
||||
@Nullable
|
||||
public Statistics getStatistics() {
|
||||
// FIXME: Check age of statistics.
|
||||
if (statistics == null)
|
||||
if (statistics == null || statistics.isStale())
|
||||
TunnelManager.getTunnelStatistics(this).whenComplete(ExceptionLoggers.E);
|
||||
return statistics;
|
||||
}
|
||||
|
||||
public CompletionStage<Statistics> getStatisticsAsync() {
|
||||
// FIXME: Check age of statistics.
|
||||
if (statistics == null)
|
||||
if (statistics == null || statistics.isStale())
|
||||
return TunnelManager.getTunnelStatistics(this);
|
||||
return CompletableFuture.completedFuture(statistics);
|
||||
}
|
||||
@@ -154,5 +158,48 @@ public class Tunnel extends BaseObservable implements Keyed<String> {
|
||||
}
|
||||
|
||||
public static class Statistics extends BaseObservable {
|
||||
private long lastTouched = SystemClock.elapsedRealtime();
|
||||
private final Map<Key, Pair<Long, Long>> peerBytes = new HashMap<>();
|
||||
|
||||
public void add(final Key key, final long rx, final long tx) {
|
||||
peerBytes.put(key, Pair.create(rx, tx));
|
||||
lastTouched = SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
||||
private boolean isStale() {
|
||||
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;
|
||||
return peerBytes.get(peer).first;
|
||||
}
|
||||
|
||||
public long peerTx(final Key peer) {
|
||||
if (!peerBytes.containsKey(peer))
|
||||
return 0;
|
||||
return peerBytes.get(peer).second;
|
||||
}
|
||||
|
||||
public long totalRx() {
|
||||
long rx = 0;
|
||||
for (final Pair<Long, Long> val : peerBytes.values()) {
|
||||
rx += val.first;
|
||||
}
|
||||
return rx;
|
||||
}
|
||||
|
||||
public long totalTx() {
|
||||
long tx = 0;
|
||||
for (final Pair<Long, Long> val : peerBytes.values()) {
|
||||
tx += val.second;
|
||||
}
|
||||
return tx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.preference;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.BuildConfig;
|
||||
import com.wireguard.android.R;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
public class DonatePreference extends Preference {
|
||||
public DonatePreference(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() { return getContext().getString(R.string.donate_summary); }
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() { return getContext().getString(R.string.donate_title); }
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse("https://www.wireguard.com/donations/"));
|
||||
try {
|
||||
getContext().startActivity(intent);
|
||||
} catch (final ActivityNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -62,6 +62,7 @@ public class DownloadsFileSaver {
|
||||
final OutputStream contentStream = contentResolver.openOutputStream(contentUri);
|
||||
if (contentStream == null)
|
||||
throw new IOException(context.getString(R.string.create_downloads_file_error));
|
||||
@SuppressWarnings("deprecation")
|
||||
Cursor cursor = contentResolver.query(contentUri, new String[]{MediaColumns.DATA}, null, null, null);
|
||||
String path = null;
|
||||
if (cursor != null) {
|
||||
@@ -86,6 +87,7 @@ public class DownloadsFileSaver {
|
||||
}
|
||||
return new DownloadsFile(context, contentStream, path, contentUri);
|
||||
} else {
|
||||
@SuppressWarnings("deprecation")
|
||||
final File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
final File file = new File(path, name);
|
||||
if (!path.isDirectory() && !path.mkdirs())
|
||||
|
||||
@@ -10,7 +10,6 @@ import android.system.OsConstants;
|
||||
import android.util.Base64;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.BuildConfig;
|
||||
import com.wireguard.android.util.RootShell.NoRootException;
|
||||
|
||||
import net.i2p.crypto.eddsa.EdDSAEngine;
|
||||
@@ -23,7 +22,6 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -130,10 +128,8 @@ public class ModuleLoader {
|
||||
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));
|
||||
final String userAgent = String.format("WireGuard/%s (Android)", BuildConfig.VERSION_NAME); //TODO: expand a bit
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection)new URL(MODULE_LIST_URL).openConnection();
|
||||
connection.setRequestProperty("User-Agent", userAgent);
|
||||
connection.setRequestProperty("User-Agent", Application.USER_AGENT);
|
||||
connection.connect();
|
||||
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
|
||||
throw new IOException("Hash list could not be found");
|
||||
@@ -150,7 +146,7 @@ public class ModuleLoader {
|
||||
if (!modules.containsKey(moduleName))
|
||||
return OsConstants.ENOENT;
|
||||
connection = (HttpURLConnection)new URL(String.format(MODULE_URL, moduleName)).openConnection();
|
||||
connection.setRequestProperty("User-Agent", userAgent);
|
||||
connection.setRequestProperty("User-Agent", Application.USER_AGENT);
|
||||
connection.connect();
|
||||
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
|
||||
throw new IOException("Module file could not be found, despite being on hash list");
|
||||
@@ -162,7 +158,7 @@ public class ModuleLoader {
|
||||
tempFile = File.createTempFile("UNVERIFIED-", null, tmpDir);
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
try (final InputStream inputStream = connection.getInputStream();
|
||||
final OutputStream outputStream = new FileOutputStream(tempFile)) {
|
||||
final FileOutputStream outputStream = new FileOutputStream(tempFile)) {
|
||||
int total = 0;
|
||||
while ((len = inputStream.read(input)) > 0) {
|
||||
total += len;
|
||||
@@ -171,6 +167,7 @@ public class ModuleLoader {
|
||||
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");
|
||||
|
||||
@@ -54,6 +54,7 @@ public final class SharedLibraryLoader {
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, len);
|
||||
}
|
||||
out.getFD().sync();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -149,18 +149,22 @@ 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], files[i]))
|
||||
if (!SharedLibraryLoader.extractLibrary(context, EXECUTABLES[i], tempFiles[i]))
|
||||
throw new FileNotFoundException("Unable to find " + EXECUTABLES[i]);
|
||||
if (!files[i].setExecutable(true, false))
|
||||
throw new IOException("Unable to mark " + files[i].getAbsolutePath() + " as executable");
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -124,6 +124,7 @@ public class SlashDrawable extends Drawable {
|
||||
return mDrawable.getIntrinsicWidth();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.OPAQUE;
|
||||
|
||||
@@ -6,35 +6,34 @@
|
||||
package com.wireguard.config;
|
||||
|
||||
import android.os.Build;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Utility methods for creating instances of {@link InetAddress}.
|
||||
*/
|
||||
public final class InetAddresses {
|
||||
private static Method PARSER_METHOD;
|
||||
@Nullable private static final Method PARSER_METHOD;
|
||||
private static final Pattern WONT_TOUCH_RESOLVER = Pattern.compile("^(((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?)|((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))$");
|
||||
|
||||
|
||||
private static Method getParserMethod() {
|
||||
if (PARSER_METHOD != null)
|
||||
return PARSER_METHOD;
|
||||
static {
|
||||
Method m = null;
|
||||
try {
|
||||
// This method is only present on Android.
|
||||
// noinspection JavaReflectionMemberAccess
|
||||
PARSER_METHOD = InetAddress.class.getMethod("parseNumericAddress", String.class);
|
||||
} catch (final NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
|
||||
// noinspection JavaReflectionMemberAccess
|
||||
m = InetAddress.class.getMethod("parseNumericAddress", String.class);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
return PARSER_METHOD;
|
||||
PARSER_METHOD = m;
|
||||
}
|
||||
|
||||
private InetAddresses() {
|
||||
// Prevent instantiation.
|
||||
}
|
||||
private InetAddresses() { }
|
||||
|
||||
/**
|
||||
* Parses a numeric IPv4 or IPv6 address without performing any DNS lookups.
|
||||
@@ -46,19 +45,28 @@ public final class InetAddresses {
|
||||
if (address.isEmpty())
|
||||
throw new ParseException(InetAddress.class, address, "Empty address");
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
|
||||
return (InetAddress) getParserMethod().invoke(null, address);
|
||||
else
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
return android.net.InetAddresses.parseNumericAddress(address);
|
||||
} catch (final IllegalAccessException | InvocationTargetException e) {
|
||||
else if (PARSER_METHOD != null)
|
||||
return (InetAddress) PARSER_METHOD.invoke(null, address);
|
||||
else
|
||||
throw new NoSuchMethodException("parseNumericAddress");
|
||||
} catch (final IllegalArgumentException e) {
|
||||
throw new ParseException(InetAddress.class, address, e);
|
||||
} catch (final Exception e) {
|
||||
final Throwable cause = e.getCause();
|
||||
// Re-throw parsing exceptions with the original type, as callers might try to catch
|
||||
// them. On the other hand, callers cannot be expected to handle reflection failures.
|
||||
if (cause instanceof IllegalArgumentException)
|
||||
throw new ParseException(InetAddress.class, address, cause);
|
||||
throw new RuntimeException(e);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
throw new ParseException(InetAddress.class, address, e);
|
||||
try {
|
||||
if (WONT_TOUCH_RESOLVER.matcher(address).matches())
|
||||
return InetAddress.getByName(address);
|
||||
else
|
||||
throw new ParseException(InetAddress.class, address, "Not an IP address");
|
||||
} catch (final UnknownHostException f) {
|
||||
throw new ParseException(InetAddress.class, address, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package com.wireguard.crypto;
|
||||
|
||||
import com.wireguard.crypto.KeyFormatException.Type;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -247,6 +248,24 @@ 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.
|
||||
*/
|
||||
|
||||
@@ -125,6 +125,7 @@
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/peers_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
|
||||
@@ -89,6 +89,24 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/endpoint_label"
|
||||
android:text="@{item.endpoint}" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/transfer_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/endpoint_text"
|
||||
android:layout_marginTop="8dp"
|
||||
android:labelFor="@+id/transfer_text"
|
||||
android:text="@string/transfer"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/transfer_text"
|
||||
style="?android:attr/textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/transfer_label"
|
||||
android:visibility="gone" />
|
||||
</RelativeLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</layout>
|
||||
|
||||
@@ -67,8 +67,6 @@
|
||||
<string name="dark_theme_title">Use dark theme</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="deselect_all">Deselect All</string>
|
||||
<string name="donate_title">♥ Donate to the WireGuard Project</string>
|
||||
<string name="donate_summary">Every contribution helps.</string>
|
||||
<string name="dns_servers">DNS servers</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="endpoint">Endpoint</string>
|
||||
@@ -146,6 +144,13 @@
|
||||
<string name="tools_installer_title">Install command line tools</string>
|
||||
<string name="tools_installer_working">Installing wg and wg-quick</string>
|
||||
<string name="tools_unavailable_error">Required tools unavailable</string>
|
||||
<string name="transfer">Transfer</string>
|
||||
<string name="transfer_rx_tx">rx: %s, tx: %s</string>
|
||||
<string name="transfer_bytes">%d B</string>
|
||||
<string name="transfer_kibibytes">%.2f KiB</string>
|
||||
<string name="transfer_mibibytes">%.2f MiB</string>
|
||||
<string name="transfer_gibibytes">%.2f GiB</string>
|
||||
<string name="transfer_tibibytes">%.2f TiB</string>
|
||||
<string name="tun_create_error">Unable to create tun device</string>
|
||||
<string name="tunnel_config_error">Unable to configure tunnel (wg-quick returned %d)</string>
|
||||
<string name="tunnel_create_error">Unable to create tunnel: %s</string>
|
||||
|
||||
@@ -16,5 +16,4 @@
|
||||
android:summaryOff="@string/dark_theme_summary_off"
|
||||
android:summaryOn="@string/dark_theme_summary_on"
|
||||
android:title="@string/dark_theme_title" />
|
||||
<com.wireguard.android.preference.DonatePreference />
|
||||
</androidx.preference.PreferenceScreen>
|
||||
|
||||
@@ -20,7 +20,7 @@ export GOARCH := $(NDK_GO_ARCH_MAP_$(ANDROID_ARCH_NAME))
|
||||
export GOOS := android
|
||||
export CGO_ENABLED := 1
|
||||
|
||||
DESIRED_GO_VERSION := 1.13.1
|
||||
DESIRED_GO_VERSION := 1.13.3
|
||||
|
||||
default: $(DESTDIR)/libwg-go.so
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"bytes"
|
||||
"log"
|
||||
"math"
|
||||
"net"
|
||||
@@ -168,6 +169,22 @@ func wgGetSocketV6(tunnelHandle int32) int32 {
|
||||
return int32(fd)
|
||||
}
|
||||
|
||||
//export wgGetConfig
|
||||
func wgGetConfig(tunnelHandle int32) *C.char {
|
||||
handle, ok := tunnelHandles[tunnelHandle]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
settings := new(bytes.Buffer)
|
||||
writer := bufio.NewWriter(settings)
|
||||
err := handle.device.IpcGetOperation(writer)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
writer.Flush()
|
||||
return C.CString(settings.String())
|
||||
}
|
||||
|
||||
//export wgVersion
|
||||
func wgVersion() *C.char {
|
||||
return C.CString(device.WireGuardGoVersion)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
module golang.zx2c4.com/wireguard/android
|
||||
|
||||
go 1.12
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect
|
||||
golang.org/x/net v0.0.0-20191011234655-491137f69257 // indirect
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47
|
||||
golang.zx2c4.com/wireguard v0.0.20191012
|
||||
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 // indirect
|
||||
golang.org/x/sys v0.0.0-20191018095205-727590c5006e
|
||||
golang.zx2c4.com/wireguard v0.0.20191013-0.20191017134306-ae492d1b3599
|
||||
)
|
||||
|
||||
@@ -4,16 +4,16 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191011234655-491137f69257 h1:ry8e2D+cwaV6hk7lb3aRTjjZo24shrbK0e11QEOkTIg=
|
||||
golang.org/x/net v0.0.0-20191011234655-491137f69257/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 h1:p9xBe/w/OzkeYVKm234g55gMdD1nSIooTir5kV11kfA=
|
||||
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191018095205-727590c5006e h1:ZtoklVMHQy6BFRHkbG6JzK+S6rX82//Yeok1vMlizfQ=
|
||||
golang.org/x/sys v0.0.0-20191018095205-727590c5006e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.zx2c4.com/wireguard v0.0.20191012 h1:sdX+y3hrHkW8KJkjY7ZgzpT5Tqo8XnBkH55U1klphko=
|
||||
golang.zx2c4.com/wireguard v0.0.20191012/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4=
|
||||
golang.zx2c4.com/wireguard v0.0.20191013-0.20191017134306-ae492d1b3599 h1:fHOjo1GyHRI2q/LcFB9NiFTqXB7WRiu2idShqqTwrZ4=
|
||||
golang.zx2c4.com/wireguard v0.0.20191013-0.20191017134306-ae492d1b3599/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4=
|
||||
|
||||
@@ -12,6 +12,7 @@ extern int wgTurnOn(struct go_string ifname, int tun_fd, struct go_string settin
|
||||
extern void wgTurnOff(int handle);
|
||||
extern int wgGetSocketV4(int handle);
|
||||
extern int wgGetSocketV6(int handle);
|
||||
extern char *wgGetConfig(int handle);
|
||||
extern char *wgVersion();
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOn(JNIEnv *env, jclass c, jstring ifname, jint tun_fd, jstring settings)
|
||||
@@ -47,6 +48,17 @@ JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgGetSocketV
|
||||
return wgGetSocketV6(handle);
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_com_wireguard_android_backend_GoBackend_wgGetConfig(JNIEnv *env, jclass c, jint handle)
|
||||
{
|
||||
jstring ret;
|
||||
char *config = wgGetConfig(handle);
|
||||
if (!config)
|
||||
return NULL;
|
||||
ret = (*env)->NewStringUTF(env, config);
|
||||
free(config);
|
||||
return ret;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_com_wireguard_android_backend_GoBackend_wgVersion(JNIEnv *env, jclass c)
|
||||
{
|
||||
jstring ret;
|
||||
|
||||
+1
-1
Submodule app/tools/wireguard updated: 8eb8443709...0b27b1d315
+1
-1
@@ -7,7 +7,7 @@ allprojects {
|
||||
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
||||
classpath 'com.android.tools.build:gradle:3.5.1'
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
|
||||
Reference in New Issue
Block a user