mirror of
https://github.com/amnezia-vpn/amneziawg-android.git
synced 2026-07-03 14:05:13 +02:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b83538d08d | |||
| 3c31c340d8 | |||
| 59620456ee | |||
| e42bd29382 | |||
| 18dbc21f96 | |||
| 68d871c47c | |||
| a45a219e5f | |||
| 49788240aa | |||
| 52166500fd | |||
| 3c8fef2655 | |||
| 21af2f2f62 | |||
| 6d01296e8b | |||
| 749efcde21 | |||
| 3af2420da9 | |||
| d0d24f4554 | |||
| 96b44c1771 | |||
| 4a1d07b364 | |||
| 7fbe5349a2 | |||
| d8bad72fd6 | |||
| 927b32c99f | |||
| 27b691bef6 | |||
| d2b9de740d | |||
| eb45b4b1bd | |||
| c545b5e65f | |||
| 992b6486a1 | |||
| 421b1f889b | |||
| 1ee1368e18 | |||
| 1a6a8789c1 | |||
| b5c155db1b | |||
| 6c5b46eadd | |||
| 5abbab2635 | |||
| e5766094f4 | |||
| 96d77988d3 | |||
| e9e4fd4e8b | |||
| b5d9fbf1f4 | |||
| 81dc89f85b | |||
| 76305045db | |||
| c4ba48d7a9 | |||
| d5dcdf13bf | |||
| 7feb3dccbf | |||
| 2251d74fce | |||
| 2c89d3fa7f | |||
| 49cc634678 | |||
| c93e81c632 | |||
| 050e202291 | |||
| efe04d6602 | |||
| 823b8324c7 | |||
| ae2f88a9ee | |||
| 32287c60c4 | |||
| 69c6fa0a24 | |||
| c3e63df7b5 | |||
| fb4f1e30d5 | |||
| 6d0fde218b | |||
| 6d02b0a26d | |||
| 495bf9c954 | |||
| cb1bc95e6b | |||
| 6fdf0266cf | |||
| ee26198e2c | |||
| fe424197da | |||
| 053ca232aa | |||
| 2e8d566bd4 | |||
| dcb0e9b3e8 | |||
| 3497882ea6 | |||
| dc2463a0ab |
+1
-1
@@ -6,7 +6,7 @@
|
||||
/captures/
|
||||
/local.properties
|
||||
.DS_Store
|
||||
.externalNativeBuild/
|
||||
.cxx/
|
||||
Thumbs.db
|
||||
build/
|
||||
*.apk
|
||||
|
||||
@@ -4,6 +4,3 @@
|
||||
[submodule "app/tools/wireguard"]
|
||||
path = app/tools/wireguard
|
||||
url = https://git.zx2c4.com/WireGuard
|
||||
[submodule "app/tools/wireguard-go"]
|
||||
path = app/tools/wireguard-go
|
||||
url = https://git.zx2c4.com/wireguard-go
|
||||
|
||||
Generated
+3
-38
@@ -29,16 +29,10 @@
|
||||
<emptyLine />
|
||||
</value>
|
||||
</option>
|
||||
<option name="RIGHT_MARGIN" value="100" />
|
||||
<AndroidXmlCodeStyleSettings>
|
||||
<option name="USE_CUSTOM_SETTINGS" value="true" />
|
||||
</AndroidXmlCodeStyleSettings>
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
||||
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
||||
<value />
|
||||
</option>
|
||||
<option name="GENERATE_FINAL_LOCALS" value="true" />
|
||||
<option name="GENERATE_FINAL_PARAMETERS" value="true" />
|
||||
<option name="INSERT_INNER_CLASS_IMPORTS" value="true" />
|
||||
<option name="IMPORT_LAYOUT_TABLE">
|
||||
<value>
|
||||
<package name="android" withSubpackages="true" static="false" />
|
||||
@@ -62,35 +56,7 @@
|
||||
</value>
|
||||
</option>
|
||||
</JavaCodeStyleSettings>
|
||||
<Objective-C-extensions>
|
||||
<file>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
|
||||
</file>
|
||||
<class>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
|
||||
</class>
|
||||
<extensions>
|
||||
<pair source="cpp" header="h" fileNamingConvention="NONE" />
|
||||
<pair source="c" header="h" fileNamingConvention="NONE" />
|
||||
</extensions>
|
||||
</Objective-C-extensions>
|
||||
<XML>
|
||||
<option name="XML_KEEP_LINE_BREAKS" value="false" />
|
||||
<option name="XML_ALIGN_ATTRIBUTES" value="false" />
|
||||
<option name="XML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
<codeStyleSettings language="JAVA">
|
||||
@@ -382,7 +348,6 @@
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
|
||||
@@ -7,9 +7,7 @@ This is an Android GUI for [WireGuard](https://www.wireguard.com/). It [opportun
|
||||
## Building
|
||||
|
||||
```
|
||||
$ git clone https://git.zx2c4.com/wireguard-android
|
||||
$ git clone --recurse-submodules https://git.zx2c4.com/wireguard-android
|
||||
$ cd wireguard-android
|
||||
$ git submodule init
|
||||
$ git submodule update
|
||||
$ ./gradlew assembleRelease
|
||||
```
|
||||
|
||||
+26
-15
@@ -6,19 +6,19 @@ apply from: 'nonnull.gradle'
|
||||
final def keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||
|
||||
android {
|
||||
buildToolsVersion '28.0.3'
|
||||
buildToolsVersion '29.0.2'
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
compileSdkVersion 28
|
||||
compileSdkVersion 29
|
||||
dataBinding.enabled true
|
||||
defaultConfig {
|
||||
applicationId 'com.wireguard.android'
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 28
|
||||
versionCode 445
|
||||
versionName '0.0.20181210'
|
||||
targetSdkVersion 29
|
||||
versionCode 455
|
||||
versionName '0.0.20191013'
|
||||
buildConfigField 'int', 'MIN_SDK_VERSION', "$minSdkVersion.apiLevel"
|
||||
}
|
||||
// If the keystore file exists
|
||||
@@ -46,6 +46,8 @@ android {
|
||||
arguments "-DANDROID_PACKAGE_NAME=${android.defaultConfig.applicationId}"
|
||||
}
|
||||
}
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
@@ -65,26 +67,35 @@ android {
|
||||
}
|
||||
|
||||
ext {
|
||||
databindingVersion = '3.1.3'
|
||||
annotationsVersion = '1.1.0'
|
||||
appcompatVersion = '1.1.0'
|
||||
cardviewVersion = '1.0.0'
|
||||
databindingVersion = '3.5.0'
|
||||
materialComponentsVersion = '1.0.0'
|
||||
jsr305Version = '3.0.2'
|
||||
streamsupportVersion = '1.6.0'
|
||||
supportLibsVersion = '27.1.1'
|
||||
threetenabpVersion = '1.1.1'
|
||||
preferenceVersion = '1.1.0'
|
||||
streamsupportVersion = '1.7.1'
|
||||
threetenabpVersion = '1.2.1'
|
||||
// ZXING switched minSdk to 24 so we cannot upgrade to 4.0.2 without following suit.
|
||||
// If you choose to upgrade to minSDK 24 then you should also disable Jetifier from
|
||||
// gradle.properties.
|
||||
zxingEmbeddedVersion = '3.6.0'
|
||||
eddsaVersion = '0.3.0'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "com.android.databinding:library:$databindingVersion"
|
||||
implementation "com.android.support:appcompat-v7:$supportLibsVersion"
|
||||
implementation "com.android.support:cardview-v7:$supportLibsVersion"
|
||||
implementation "com.android.support:design:$supportLibsVersion"
|
||||
implementation "com.android.support:preference-v14:$supportLibsVersion"
|
||||
implementation "com.android.support:support-annotations:$supportLibsVersion"
|
||||
implementation "androidx.annotation:annotation:$annotationsVersion"
|
||||
implementation "androidx.appcompat:appcompat:$appcompatVersion"
|
||||
implementation "androidx.cardview:cardview:$cardviewVersion"
|
||||
implementation "androidx.databinding:databinding-runtime:$databindingVersion"
|
||||
implementation "androidx.preference:preference:$preferenceVersion"
|
||||
implementation "com.google.android.material:material:$materialComponentsVersion"
|
||||
implementation "com.google.code.findbugs:jsr305:$jsr305Version"
|
||||
implementation "com.jakewharton.threetenabp:threetenabp:$threetenabpVersion"
|
||||
implementation "com.journeyapps:zxing-android-embedded:$zxingEmbeddedVersion"
|
||||
implementation "net.sourceforge.streamsupport:android-retrofuture:$streamsupportVersion"
|
||||
implementation "net.sourceforge.streamsupport:android-retrostreams:$streamsupportVersion"
|
||||
implementation "net.i2p.crypto:eddsa:$eddsaVersion"
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
# Squelch all warnings, they're harmless but ProGuard
|
||||
# escalates them as errors.
|
||||
-dontwarn sun.misc.Unsafe
|
||||
@@ -19,7 +19,6 @@
|
||||
<application
|
||||
android:name=".Application"
|
||||
android:allowBackup="false"
|
||||
android:extractNativeLibs="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
@@ -43,8 +42,7 @@
|
||||
<activity
|
||||
android:name=".activity.SettingsActivity"
|
||||
android:label="@string/settings"
|
||||
android:parentActivityName=".activity.MainActivity"
|
||||
android:theme="@style/SettingsTheme" />
|
||||
android:parentActivityName=".activity.MainActivity" />
|
||||
|
||||
<activity
|
||||
android:name=".activity.TunnelCreatorActivity"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -12,9 +12,9 @@ import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatDelegate;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
import com.wireguard.android.backend.Backend;
|
||||
import com.wireguard.android.backend.GoBackend;
|
||||
@@ -22,6 +22,7 @@ import com.wireguard.android.backend.WgQuickBackend;
|
||||
import com.wireguard.android.configStore.FileConfigStore;
|
||||
import com.wireguard.android.model.TunnelManager;
|
||||
import com.wireguard.android.util.AsyncWorker;
|
||||
import com.wireguard.android.util.ModuleLoader;
|
||||
import com.wireguard.android.util.RootShell;
|
||||
import com.wireguard.android.util.ToolsInstaller;
|
||||
|
||||
@@ -38,6 +39,7 @@ public class Application extends android.app.Application {
|
||||
@SuppressWarnings("NullableProblems") private RootShell rootShell;
|
||||
@SuppressWarnings("NullableProblems") private SharedPreferences sharedPreferences;
|
||||
@SuppressWarnings("NullableProblems") private ToolsInstaller toolsInstaller;
|
||||
@SuppressWarnings("NullableProblems") private ModuleLoader moduleLoader;
|
||||
@SuppressWarnings("NullableProblems") private TunnelManager tunnelManager;
|
||||
|
||||
public Application() {
|
||||
@@ -57,9 +59,19 @@ public class Application extends android.app.Application {
|
||||
synchronized (app.futureBackend) {
|
||||
if (app.backend == null) {
|
||||
Backend backend = null;
|
||||
if (new File("/sys/module/wireguard").exists()) {
|
||||
boolean didStartRootShell = false;
|
||||
if (!app.moduleLoader.isModuleLoaded() && app.moduleLoader.moduleMightExist()) {
|
||||
try {
|
||||
app.rootShell.start();
|
||||
didStartRootShell = true;
|
||||
app.moduleLoader.loadModule();
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (app.moduleLoader.isModuleLoaded()) {
|
||||
try {
|
||||
if (!didStartRootShell)
|
||||
app.rootShell.start();
|
||||
backend = new WgQuickBackend(app.getApplicationContext());
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
@@ -87,6 +99,9 @@ public class Application extends android.app.Application {
|
||||
public static ToolsInstaller getToolsInstaller() {
|
||||
return get().toolsInstaller;
|
||||
}
|
||||
public static ModuleLoader getModuleLoader() {
|
||||
return get().moduleLoader;
|
||||
}
|
||||
|
||||
public static TunnelManager getTunnelManager() {
|
||||
return get().tunnelManager;
|
||||
@@ -113,11 +128,16 @@ public class Application extends android.app.Application {
|
||||
asyncWorker = new AsyncWorker(AsyncTask.SERIAL_EXECUTOR, new Handler(Looper.getMainLooper()));
|
||||
rootShell = new RootShell(getApplicationContext());
|
||||
toolsInstaller = new ToolsInstaller(getApplicationContext());
|
||||
moduleLoader = new ModuleLoader(getApplicationContext());
|
||||
|
||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
AppCompatDelegate.setDefaultNightMode(
|
||||
sharedPreferences.getBoolean("dark_theme", false) ?
|
||||
AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
AppCompatDelegate.setDefaultNightMode(
|
||||
sharedPreferences.getBoolean("dark_theme", false) ?
|
||||
AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO);
|
||||
} else {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
|
||||
}
|
||||
|
||||
tunnelManager = new TunnelManager(new FileConfigStore(getApplicationContext()));
|
||||
tunnelManager.onCreate();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -7,8 +7,8 @@ package com.wireguard.android;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Intent;
|
||||
import android.databinding.Observable;
|
||||
import android.databinding.Observable.OnPropertyChangedCallback;
|
||||
import androidx.databinding.Observable;
|
||||
import androidx.databinding.Observable.OnPropertyChangedCallback;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Icon;
|
||||
@@ -16,14 +16,14 @@ import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.service.quicksettings.TileService;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.wireguard.android.activity.MainActivity;
|
||||
import com.wireguard.android.model.Tunnel;
|
||||
import com.wireguard.android.model.Tunnel.State;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.android.util.ErrorMessages;
|
||||
import com.wireguard.android.widget.SlashDrawable;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -114,7 +114,7 @@ public class QuickTileService extends TileService {
|
||||
@Nullable final Throwable throwable) {
|
||||
if (throwable == null)
|
||||
return;
|
||||
final String error = ExceptionLoggers.unwrapMessage(throwable);
|
||||
final String error = ErrorMessages.get(throwable);
|
||||
final String message = getString(R.string.toggle_error, error);
|
||||
Log.e(TAG, message, throwable);
|
||||
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.activity;
|
||||
|
||||
import android.databinding.CallbackRegistry;
|
||||
import android.databinding.CallbackRegistry.NotifierCallback;
|
||||
import androidx.databinding.CallbackRegistry;
|
||||
import androidx.databinding.CallbackRegistry.NotifierCallback;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.model.Tunnel;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -8,10 +8,10 @@ package com.wireguard.android.activity;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.activity;
|
||||
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||
import android.support.v7.preference.PreferenceScreen;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import android.util.SparseArray;
|
||||
import android.view.MenuItem;
|
||||
|
||||
@@ -91,13 +92,16 @@ public class SettingsActivity extends ThemeChangeAwareActivity {
|
||||
@Override
|
||||
public void onCreatePreferences(final Bundle savedInstanceState, final String key) {
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
final PreferenceScreen screen = getPreferenceScreen();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
screen.removePreference(getPreferenceManager().findPreference("dark_theme"));
|
||||
|
||||
final Preference wgQuickOnlyPrefs[] = {
|
||||
getPreferenceManager().findPreference("tools_installer"),
|
||||
getPreferenceManager().findPreference("restore_on_boot")
|
||||
};
|
||||
for (final Preference pref : wgQuickOnlyPrefs)
|
||||
pref.setVisible(false);
|
||||
final PreferenceScreen screen = getPreferenceScreen();
|
||||
Application.getBackendAsync().thenAccept(backend -> {
|
||||
for (final Preference pref : wgQuickOnlyPrefs) {
|
||||
if (backend instanceof WgQuickBackend)
|
||||
@@ -106,6 +110,19 @@ public class SettingsActivity extends ThemeChangeAwareActivity {
|
||||
screen.removePreference(pref);
|
||||
}
|
||||
});
|
||||
|
||||
final Preference moduleInstaller = getPreferenceManager().findPreference("module_downloader");
|
||||
moduleInstaller.setVisible(false);
|
||||
if (Application.getModuleLoader().isModuleLoaded()) {
|
||||
screen.removePreference(moduleInstaller);
|
||||
} else {
|
||||
Application.getAsyncWorker().runAsync(Application.getRootShell()::start).whenComplete((v, e) -> {
|
||||
if (e == null)
|
||||
moduleInstaller.setVisible(true);
|
||||
else
|
||||
screen.removePreference(moduleInstaller);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -7,10 +7,11 @@ package com.wireguard.android.activity;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.app.AppCompatDelegate;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
@@ -55,12 +56,14 @@ public abstract class ThemeChangeAwareActivity extends AppCompatActivity impleme
|
||||
@Override
|
||||
protected void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Application.getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
|
||||
Application.getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
Application.getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
|
||||
Application.getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.wireguard.android.fragment.TunnelEditorFragment;
|
||||
import com.wireguard.android.model.Tunnel;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -9,8 +9,8 @@ import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.util.ArraySet;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.collection.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
@@ -27,7 +27,6 @@ import com.wireguard.config.Peer;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -96,7 +95,7 @@ public final class GoBackend implements Backend {
|
||||
|
||||
@Override
|
||||
public String getTypePrettyName() {
|
||||
return context.getResources().getString(R.string.type_name_go_userspace);
|
||||
return context.getString(R.string.type_name_go_userspace);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -112,7 +111,7 @@ public final class GoBackend implements Backend {
|
||||
if (state == originalState)
|
||||
return originalState;
|
||||
if (state == State.UP && currentTunnel != null)
|
||||
throw new IllegalStateException(context.getResources().getString(R.string.multiple_tunnels_error));
|
||||
throw new IllegalStateException(context.getString(R.string.multiple_tunnels_error));
|
||||
Log.d(TAG, "Changing tunnel " + tunnel.getName() + " to state " + state);
|
||||
setStateInternal(tunnel, tunnel.getConfig(), state);
|
||||
return getState(tunnel);
|
||||
@@ -124,10 +123,10 @@ public final class GoBackend implements Backend {
|
||||
if (state == State.UP) {
|
||||
Log.i(TAG, "Bringing tunnel up");
|
||||
|
||||
Objects.requireNonNull(config, context.getResources().getString(R.string.no_config_error));
|
||||
Objects.requireNonNull(config, context.getString(R.string.no_config_error));
|
||||
|
||||
if (VpnService.prepare(context) != null)
|
||||
throw new Exception(context.getResources().getString(R.string.vpn_not_authed_error));
|
||||
throw new Exception(context.getString(R.string.vpn_not_authorized_error));
|
||||
|
||||
final VpnService service;
|
||||
if (!vpnService.isDone())
|
||||
@@ -136,7 +135,7 @@ public final class GoBackend implements Backend {
|
||||
try {
|
||||
service = vpnService.get(2, TimeUnit.SECONDS);
|
||||
} catch (final TimeoutException e) {
|
||||
throw new Exception(context.getResources().getString(R.string.vpn_start_error), e);
|
||||
throw new Exception(context.getString(R.string.vpn_start_error), e);
|
||||
}
|
||||
|
||||
if (currentTunnelHandle != -1) {
|
||||
@@ -174,12 +173,12 @@ public final class GoBackend implements Backend {
|
||||
builder.setBlocking(true);
|
||||
try (final ParcelFileDescriptor tun = builder.establish()) {
|
||||
if (tun == null)
|
||||
throw new Exception(context.getResources().getString(R.string.tun_create_error));
|
||||
throw new Exception(context.getString(R.string.tun_create_error));
|
||||
Log.d(TAG, "Go backend v" + wgVersion());
|
||||
currentTunnelHandle = wgTurnOn(tunnel.getName(), tun.detachFd(), goConfig);
|
||||
}
|
||||
if (currentTunnelHandle < 0)
|
||||
throw new Exception(String.format(Locale.getDefault(), context.getResources().getString(R.string.tunnel_on_error), currentTunnelHandle));
|
||||
throw new Exception(context.getString(R.string.tunnel_on_error, currentTunnelHandle));
|
||||
|
||||
currentTunnel = tunnel;
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.backend;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
@@ -88,7 +88,7 @@ public final class WgQuickBackend implements Backend {
|
||||
|
||||
@Override
|
||||
public String getTypePrettyName() {
|
||||
return context.getResources().getString(R.string.type_name_kernel_module);
|
||||
return context.getString(R.string.type_name_kernel_module);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,7 +96,7 @@ public final class WgQuickBackend implements Backend {
|
||||
final List<String> output = new ArrayList<>();
|
||||
if (Application.getRootShell()
|
||||
.run(output, "cat /sys/module/wireguard/version") != 0 || output.isEmpty())
|
||||
throw new Exception(context.getResources().getString(R.string.module_version_error));
|
||||
throw new Exception(context.getString(R.string.module_version_error));
|
||||
return output.get(0);
|
||||
}
|
||||
|
||||
@@ -128,6 +128,6 @@ public final class WgQuickBackend implements Backend {
|
||||
// noinspection ResultOfMethodCallIgnored
|
||||
tempFile.delete();
|
||||
if (result != 0)
|
||||
throw new Exception(context.getResources().getString(R.string.tunnel_config_error));
|
||||
throw new Exception(context.getString(R.string.tunnel_config_error, result));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -9,8 +9,8 @@ import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.config.BadConfigException;
|
||||
import com.wireguard.config.Config;
|
||||
import com.wireguard.config.ParseException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -18,7 +18,6 @@ import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import java9.util.stream.Collectors;
|
||||
@@ -42,8 +41,7 @@ public final class FileConfigStore implements ConfigStore {
|
||||
Log.d(TAG, "Creating configuration for tunnel " + name);
|
||||
final File file = fileFor(name);
|
||||
if (!file.createNewFile())
|
||||
throw new IOException(String.format(Locale.getDefault(),
|
||||
context.getResources().getString(R.string.config_file_exists_error), file.getName()));
|
||||
throw new IOException(context.getString(R.string.config_file_exists_error, file.getName()));
|
||||
try (final FileOutputStream stream = new FileOutputStream(file, false)) {
|
||||
stream.write(config.toWgQuickString().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
@@ -55,8 +53,7 @@ public final class FileConfigStore implements ConfigStore {
|
||||
Log.d(TAG, "Deleting configuration for tunnel " + name);
|
||||
final File file = fileFor(name);
|
||||
if (!file.delete())
|
||||
throw new IOException(String.format(Locale.getDefault(),
|
||||
context.getResources().getString(R.string.config_delete_error), file.getName()));
|
||||
throw new IOException(context.getString(R.string.config_delete_error, file.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,7 +69,7 @@ public final class FileConfigStore implements ConfigStore {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Config load(final String name) throws IOException, ParseException {
|
||||
public Config load(final String name) throws BadConfigException, IOException {
|
||||
try (final FileInputStream stream = new FileInputStream(fileFor(name))) {
|
||||
return Config.parse(stream);
|
||||
}
|
||||
@@ -84,13 +81,11 @@ public final class FileConfigStore implements ConfigStore {
|
||||
final File file = fileFor(name);
|
||||
final File replacementFile = fileFor(replacement);
|
||||
if (!replacementFile.createNewFile())
|
||||
throw new IOException(String.format(Locale.getDefault(),
|
||||
context.getResources().getString(R.string.config_exists_error), replacement));
|
||||
throw new IOException(context.getString(R.string.config_exists_error, replacement));
|
||||
if (!file.renameTo(replacementFile)) {
|
||||
if (!replacementFile.delete())
|
||||
Log.w(TAG, "Couldn't delete marker file for new name " + replacement);
|
||||
throw new IOException(String.format(Locale.getDefault(),
|
||||
context.getResources().getString(R.string.config_rename_error), file.getName()));
|
||||
throw new IOException(context.getString(R.string.config_rename_error, file.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,8 +94,7 @@ public final class FileConfigStore implements ConfigStore {
|
||||
Log.d(TAG, "Saving configuration for tunnel " + name);
|
||||
final File file = fileFor(name);
|
||||
if (!file.isFile())
|
||||
throw new FileNotFoundException(String.format(Locale.getDefault(),
|
||||
context.getResources().getString(R.string.config_not_found_error), file.getName()));
|
||||
throw new FileNotFoundException(context.getString(R.string.config_not_found_error, file.getName()));
|
||||
try (final FileOutputStream stream = new FileOutputStream(file, false)) {
|
||||
stream.write(config.toWgQuickString().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.databinding;
|
||||
|
||||
import android.databinding.BindingAdapter;
|
||||
import android.databinding.DataBindingUtil;
|
||||
import android.databinding.ObservableList;
|
||||
import android.databinding.ViewDataBinding;
|
||||
import android.databinding.adapters.ListenerUtil;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import androidx.databinding.BindingAdapter;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.databinding.ObservableList;
|
||||
import androidx.databinding.ViewDataBinding;
|
||||
import androidx.databinding.adapters.ListenerUtil;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.text.InputFilter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.databinding;
|
||||
|
||||
import android.databinding.DataBindingUtil;
|
||||
import android.databinding.ObservableList;
|
||||
import android.databinding.ViewDataBinding;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.databinding.ObservableList;
|
||||
import androidx.databinding.ViewDataBinding;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
+7
-7
@@ -1,17 +1,17 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.databinding;
|
||||
|
||||
import android.content.Context;
|
||||
import android.databinding.DataBindingUtil;
|
||||
import android.databinding.ObservableList;
|
||||
import android.databinding.ViewDataBinding;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.RecyclerView.Adapter;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.databinding.ObservableList;
|
||||
import androidx.databinding.ViewDataBinding;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.Adapter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -12,17 +12,17 @@ import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.databinding.AppListDialogFragmentBinding;
|
||||
import com.wireguard.android.model.ApplicationData;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.android.util.ErrorMessages;
|
||||
import com.wireguard.android.util.ObservableKeyedArrayList;
|
||||
import com.wireguard.android.util.ObservableKeyedList;
|
||||
|
||||
@@ -73,7 +73,7 @@ public class AppListDialogFragment extends DialogFragment {
|
||||
appData.clear();
|
||||
appData.addAll(data);
|
||||
} else {
|
||||
final String error = throwable != null ? ExceptionLoggers.unwrapMessage(throwable) : "Unknown";
|
||||
final String error = ErrorMessages.get(throwable);
|
||||
final String message = activity.getString(R.string.error_fetching_apps, error);
|
||||
Toast.makeText(activity, message, Toast.LENGTH_LONG).show();
|
||||
dismissAllowingStateLoss();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -7,11 +7,11 @@ package com.wireguard.android.fragment;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.databinding.DataBindingUtil;
|
||||
import android.databinding.ViewDataBinding;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.Fragment;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.databinding.ViewDataBinding;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
@@ -25,7 +25,7 @@ import com.wireguard.android.databinding.TunnelDetailFragmentBinding;
|
||||
import com.wireguard.android.databinding.TunnelListItemBinding;
|
||||
import com.wireguard.android.model.Tunnel;
|
||||
import com.wireguard.android.model.Tunnel.State;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.android.util.ErrorMessages;
|
||||
|
||||
/**
|
||||
* Base class for fragments that need to know the currently-selected tunnel. Only does anything when
|
||||
@@ -111,7 +111,7 @@ public abstract class BaseFragment extends Fragment implements OnSelectedTunnelC
|
||||
tunnel.setState(State.of(checked)).whenComplete((state, throwable) -> {
|
||||
if (throwable == null)
|
||||
return;
|
||||
final String error = ExceptionLoggers.unwrapMessage(throwable);
|
||||
final String error = ErrorMessages.get(throwable);
|
||||
final int messageResId = checked ? R.string.error_up : R.string.error_down;
|
||||
final String message = getContext().getString(messageResId, error);
|
||||
final View view = getView();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -10,26 +10,25 @@ import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.databinding.ConfigNamingDialogFragmentBinding;
|
||||
import com.wireguard.config.BadConfigException;
|
||||
import com.wireguard.config.Config;
|
||||
import com.wireguard.config.ParseException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ConfigNamingDialogFragment extends DialogFragment {
|
||||
|
||||
private static final String KEY_CONFIG_TEXT = "config_text";
|
||||
|
||||
@Nullable private ConfigNamingDialogFragmentBinding binding;
|
||||
@Nullable private Config config;
|
||||
@Nullable private InputMethodManager imm;
|
||||
@@ -66,24 +65,26 @@ public class ConfigNamingDialogFragment extends DialogFragment {
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final Bundle arguments = getArguments();
|
||||
final String configText = arguments.getString(KEY_CONFIG_TEXT);
|
||||
final byte[] configBytes = configText.getBytes(StandardCharsets.UTF_8);
|
||||
try {
|
||||
config = Config.parse(new ByteArrayInputStream(getArguments().getString(KEY_CONFIG_TEXT).getBytes(StandardCharsets.UTF_8)));
|
||||
} catch (final IOException | ParseException exception) {
|
||||
throw new RuntimeException(String.format(Locale.getDefault(),
|
||||
getResources().getString(R.string.invalid_config_error), getClass().getSimpleName()), exception);
|
||||
config = Config.parse(new ByteArrayInputStream(configBytes));
|
||||
} catch (final BadConfigException | IOException e) {
|
||||
throw new IllegalArgumentException("Invalid config passed to " + getClass().getSimpleName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(final Bundle savedInstanceState) {
|
||||
final Activity activity = getActivity();
|
||||
final Activity activity = Objects.requireNonNull(getActivity());
|
||||
|
||||
imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
|
||||
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity);
|
||||
alertDialogBuilder.setTitle(R.string.import_from_qrcode);
|
||||
alertDialogBuilder.setTitle(R.string.import_from_qr_code);
|
||||
|
||||
binding = ConfigNamingDialogFragmentBinding.inflate(getActivity().getLayoutInflater(), null, false);
|
||||
binding = ConfigNamingDialogFragmentBinding.inflate(activity.getLayoutInflater(), null, false);
|
||||
binding.executePendingBindings();
|
||||
alertDialogBuilder.setView(binding.getRoot());
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -7,11 +7,11 @@ package com.wireguard.android.fragment;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.databinding.ObservableList;
|
||||
import androidx.databinding.ObservableList;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
@@ -28,7 +28,7 @@ import com.wireguard.android.databinding.TunnelEditorFragmentBinding;
|
||||
import com.wireguard.android.fragment.AppListDialogFragment.AppExclusionListener;
|
||||
import com.wireguard.android.model.Tunnel;
|
||||
import com.wireguard.android.model.TunnelManager;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.android.util.ErrorMessages;
|
||||
import com.wireguard.android.viewmodel.ConfigProxy;
|
||||
import com.wireguard.config.Config;
|
||||
|
||||
@@ -63,7 +63,7 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
|
||||
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
|
||||
onFinished();
|
||||
} else {
|
||||
final String error = ExceptionLoggers.unwrapMessage(throwable);
|
||||
final String error = ErrorMessages.get(throwable);
|
||||
message = getString(R.string.config_save_error, savedTunnel.getName(), error);
|
||||
Log.e(TAG, message, throwable);
|
||||
if (binding != null) {
|
||||
@@ -140,7 +140,7 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
|
||||
try {
|
||||
newConfig = binding.getConfig().resolve();
|
||||
} catch (final Exception e) {
|
||||
final String error = ExceptionLoggers.unwrapMessage(e);
|
||||
final String error = ErrorMessages.get(e);
|
||||
final String tunnelName = tunnel == null ? binding.getName() : tunnel.getName();
|
||||
final String message = getString(R.string.config_save_error, tunnelName, error);
|
||||
Log.e(TAG, message, e);
|
||||
@@ -208,7 +208,7 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
|
||||
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
|
||||
onFinished();
|
||||
} else {
|
||||
final String error = ExceptionLoggers.unwrapMessage(throwable);
|
||||
final String error = ErrorMessages.get(throwable);
|
||||
message = getString(R.string.tunnel_create_error, error);
|
||||
Log.e(TAG, message, throwable);
|
||||
if (binding != null) {
|
||||
@@ -227,7 +227,7 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
|
||||
Log.d(TAG, "Attempting to save config of renamed tunnel " + tunnel.getName());
|
||||
renamedTunnel.setConfig(newConfig).whenComplete((a, b) -> onConfigSaved(renamedTunnel, b));
|
||||
} else {
|
||||
final String error = ExceptionLoggers.unwrapMessage(throwable);
|
||||
final String error = ErrorMessages.get(throwable);
|
||||
message = getString(R.string.tunnel_rename_error, error);
|
||||
Log.e(TAG, message, throwable);
|
||||
if (binding != null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -14,13 +14,13 @@ import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
@@ -37,11 +37,11 @@ import com.wireguard.android.databinding.ObservableKeyedRecyclerViewAdapter;
|
||||
import com.wireguard.android.databinding.TunnelListFragmentBinding;
|
||||
import com.wireguard.android.databinding.TunnelListItemBinding;
|
||||
import com.wireguard.android.model.Tunnel;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.android.util.ErrorMessages;
|
||||
import com.wireguard.android.widget.MultiselectableRelativeLayout;
|
||||
import com.wireguard.android.widget.fab.FloatingActionsMenuRecyclerViewScrollListener;
|
||||
import com.wireguard.config.BadConfigException;
|
||||
import com.wireguard.config.Config;
|
||||
import com.wireguard.config.ParseException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -89,8 +89,8 @@ public class TunnelListFragment extends BaseFragment {
|
||||
final FragmentManager fragmentManager = getFragmentManager();
|
||||
if (fragmentManager != null)
|
||||
ConfigNamingDialogFragment.newInstance(configText).show(fragmentManager, null);
|
||||
} catch (final IllegalArgumentException | IOException | ParseException exception) {
|
||||
onTunnelImportFinished(Collections.emptyList(), Collections.singletonList(exception));
|
||||
} catch (final BadConfigException | IOException e) {
|
||||
onTunnelImportFinished(Collections.emptyList(), Collections.singletonList(e));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,8 +115,7 @@ public class TunnelListFragment extends BaseFragment {
|
||||
int idx = name.lastIndexOf('/');
|
||||
if (idx >= 0) {
|
||||
if (idx >= name.length() - 1)
|
||||
throw new IllegalArgumentException(String.format(Locale.getDefault(),
|
||||
getResources().getString(R.string.illegal_filename_error), name));
|
||||
throw new IllegalArgumentException(getResources().getString(R.string.illegal_filename_error, name));
|
||||
name = name.substring(idx + 1);
|
||||
}
|
||||
boolean isZip = name.toLowerCase(Locale.ENGLISH).endsWith(".zip");
|
||||
@@ -271,7 +270,7 @@ public class TunnelListFragment extends BaseFragment {
|
||||
final IntentIntegrator intentIntegrator = IntentIntegrator.forSupportFragment(this);
|
||||
intentIntegrator.setOrientationLocked(false);
|
||||
intentIntegrator.setBeepEnabled(false);
|
||||
intentIntegrator.setPrompt(getString(R.string.qrcode_hint));
|
||||
intentIntegrator.setPrompt(getString(R.string.qr_code_hint));
|
||||
intentIntegrator.initiateScan(Collections.singletonList(IntentIntegrator.QR_CODE));
|
||||
|
||||
if (binding != null)
|
||||
@@ -302,7 +301,7 @@ public class TunnelListFragment extends BaseFragment {
|
||||
if (throwable == null) {
|
||||
message = getResources().getQuantityString(R.plurals.delete_success, count, count);
|
||||
} else {
|
||||
final String error = ExceptionLoggers.unwrapMessage(throwable);
|
||||
final String error = ErrorMessages.get(throwable);
|
||||
message = getResources().getQuantityString(R.plurals.delete_error, count, count, error);
|
||||
Log.e(TAG, message, throwable);
|
||||
}
|
||||
@@ -315,7 +314,7 @@ public class TunnelListFragment extends BaseFragment {
|
||||
String message = null;
|
||||
|
||||
for (final Throwable throwable : throwables) {
|
||||
final String error = ExceptionLoggers.unwrapMessage(throwable);
|
||||
final String error = ErrorMessages.get(throwable);
|
||||
message = getString(R.string.import_error, error);
|
||||
Log.e(TAG, message, throwable);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.model;
|
||||
|
||||
import android.databinding.BaseObservable;
|
||||
import android.databinding.Bindable;
|
||||
import androidx.databinding.BaseObservable;
|
||||
import androidx.databinding.Bindable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import com.wireguard.android.BR;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.model;
|
||||
|
||||
import android.databinding.BaseObservable;
|
||||
import android.databinding.Bindable;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.databinding.BaseObservable;
|
||||
import androidx.databinding.Bindable;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.wireguard.android.BR;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -8,9 +8,9 @@ package com.wireguard.android.model;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.databinding.BaseObservable;
|
||||
import android.databinding.Bindable;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.databinding.BaseObservable;
|
||||
import androidx.databinding.Bindable;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.BR;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -8,22 +8,21 @@ package com.wireguard.android.preference;
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v7.preference.Preference;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.android.util.DownloadsFileSaver;
|
||||
import com.wireguard.android.util.DownloadsFileSaver.DownloadsFile;
|
||||
import com.wireguard.android.util.ErrorMessages;
|
||||
import com.wireguard.android.util.FragmentUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
@@ -41,42 +40,39 @@ public class LogExporterPreference extends Preference {
|
||||
|
||||
private void exportLog() {
|
||||
Application.getAsyncWorker().supplyAsync(() -> {
|
||||
final File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
final File file = new File(path, "wireguard-log.txt");
|
||||
if (!path.isDirectory() && !path.mkdirs())
|
||||
throw new IOException(
|
||||
getContext().getResources().getString(R.string.create_output_dir_error));
|
||||
|
||||
/* We would like to simply run `builder.redirectOutput(file);`, but this is API 26.
|
||||
* Instead we have to do this dance, since logcat appends.
|
||||
*/
|
||||
new FileOutputStream(file).close();
|
||||
|
||||
DownloadsFile outputFile = DownloadsFileSaver.save(getContext(), "wireguard-log.txt", "text/plain", true);
|
||||
try {
|
||||
final Process process = Runtime.getRuntime().exec(new String[]{
|
||||
"logcat", "-b", "all", "-d", "-v", "threadtime", "-f", file.getAbsolutePath(), "*:V"});
|
||||
if (process.waitFor() != 0) {
|
||||
try (final BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
|
||||
"logcat", "-b", "all", "-d", "-v", "threadtime", "*:V"});
|
||||
try (final BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
final BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream())))
|
||||
{
|
||||
String line;
|
||||
while ((line = stdout.readLine()) != null) {
|
||||
outputFile.getOutputStream().write(line.getBytes());
|
||||
outputFile.getOutputStream().write('\n');
|
||||
}
|
||||
outputFile.getOutputStream().close();
|
||||
stdout.close();
|
||||
if (process.waitFor() != 0) {
|
||||
final StringBuilder errors = new StringBuilder();
|
||||
errors.append("Unable to run logcat: ");
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null)
|
||||
errors.append(R.string.logcat_error);
|
||||
while ((line = stderr.readLine()) != null)
|
||||
errors.append(line);
|
||||
throw new Exception(errors.toString());
|
||||
}
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
// noinspection ResultOfMethodCallIgnored
|
||||
file.delete();
|
||||
outputFile.delete();
|
||||
throw e;
|
||||
}
|
||||
return file.getAbsolutePath();
|
||||
return outputFile.getFileName();
|
||||
}).whenComplete(this::exportLogComplete);
|
||||
}
|
||||
|
||||
private void exportLogComplete(final String filePath, @Nullable final Throwable throwable) {
|
||||
if (throwable != null) {
|
||||
final String error = ExceptionLoggers.unwrapMessage(throwable);
|
||||
final String error = ErrorMessages.get(throwable);
|
||||
final String message = getContext().getString(R.string.log_export_error, error);
|
||||
Log.e(TAG, message, throwable);
|
||||
Snackbar.make(
|
||||
@@ -98,7 +94,7 @@ public class LogExporterPreference extends Preference {
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
return getContext().getString(R.string.log_exporter_title);
|
||||
return getContext().getString(R.string.log_export_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright © 2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.preference;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.system.OsConstants;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.util.ModuleLoader;
|
||||
import com.wireguard.android.util.ToolsInstaller;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
public class ModuleDownloaderPreference extends Preference {
|
||||
private State state = State.INITIAL;
|
||||
|
||||
public ModuleDownloaderPreference(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
return getContext().getString(state.messageResourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
return getContext().getString(R.string.module_installer_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
setState(State.WORKING);
|
||||
Application.getAsyncWorker().supplyAsync(Application.getModuleLoader()::download).whenComplete(this::onDownloadResult);
|
||||
}
|
||||
|
||||
private void onDownloadResult(final Integer result, @Nullable final Throwable throwable) {
|
||||
if (throwable != null) {
|
||||
setState(State.FAILURE);
|
||||
Toast.makeText(getContext(), throwable.getMessage(), Toast.LENGTH_LONG).show();
|
||||
} else if (result == OsConstants.ENOENT)
|
||||
setState(State.NOTFOUND);
|
||||
else if (result == OsConstants.EXIT_SUCCESS) {
|
||||
setState(State.SUCCESS);
|
||||
Application.getAsyncWorker().runAsync(() -> {
|
||||
Thread.sleep(1000 * 5);
|
||||
Intent i = getContext().getPackageManager().getLaunchIntentForPackage(getContext().getPackageName());
|
||||
if (i == null)
|
||||
return;
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
Application.get().startActivity(i);
|
||||
System.exit(0);
|
||||
});
|
||||
} else
|
||||
setState(State.FAILURE);
|
||||
}
|
||||
|
||||
private void setState(final State state) {
|
||||
if (this.state == state)
|
||||
return;
|
||||
this.state = state;
|
||||
if (isEnabled() != state.shouldEnableView)
|
||||
setEnabled(state.shouldEnableView);
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
private enum State {
|
||||
INITIAL(R.string.module_installer_initial, true),
|
||||
FAILURE(R.string.module_installer_error, true),
|
||||
WORKING(R.string.module_installer_working, false),
|
||||
SUCCESS(R.string.module_installer_success, false),
|
||||
NOTFOUND(R.string.module_installer_not_found, false);
|
||||
|
||||
private final int messageResourceId;
|
||||
private final boolean shouldEnableView;
|
||||
|
||||
State(final int messageResourceId, final boolean shouldEnableView) {
|
||||
this.messageResourceId = messageResourceId;
|
||||
this.shouldEnableView = shouldEnableView;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.preference;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.preference.Preference;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -9,8 +9,8 @@ import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.preference.Preference;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -8,23 +8,21 @@ package com.wireguard.android.preference;
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v7.preference.Preference;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import androidx.preference.Preference;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.model.Tunnel;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.android.util.DownloadsFileSaver;
|
||||
import com.wireguard.android.util.DownloadsFileSaver.DownloadsFile;
|
||||
import com.wireguard.android.util.ErrorMessages;
|
||||
import com.wireguard.android.util.FragmentUtils;
|
||||
import com.wireguard.config.Config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -56,19 +54,15 @@ public class ZipExporterPreference extends Preference {
|
||||
futureConfigs.add(tunnel.getConfigAsync().toCompletableFuture());
|
||||
if (futureConfigs.isEmpty()) {
|
||||
exportZipComplete(null, new IllegalArgumentException(
|
||||
getContext().getResources().getString(R.string.no_tunnels_error)));
|
||||
getContext().getString(R.string.no_tunnels_error)));
|
||||
return;
|
||||
}
|
||||
CompletableFuture.allOf(futureConfigs.toArray(new CompletableFuture[futureConfigs.size()]))
|
||||
.whenComplete((ignored1, exception) -> Application.getAsyncWorker().supplyAsync(() -> {
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
final File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
final File file = new File(path, "wireguard-export.zip");
|
||||
if (!path.isDirectory() && !path.mkdirs())
|
||||
throw new IOException(
|
||||
getContext().getResources().getString(R.string.create_output_dir_error));
|
||||
try (ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(file))) {
|
||||
DownloadsFile outputFile = DownloadsFileSaver.save(getContext(), "wireguard-export.zip", "application/zip", true);
|
||||
try (ZipOutputStream zip = new ZipOutputStream(outputFile.getOutputStream())) {
|
||||
for (int i = 0; i < futureConfigs.size(); ++i) {
|
||||
zip.putNextEntry(new ZipEntry(tunnels.get(i).getName() + ".conf"));
|
||||
zip.write(futureConfigs.get(i).getNow(null).
|
||||
@@ -76,17 +70,16 @@ public class ZipExporterPreference extends Preference {
|
||||
}
|
||||
zip.closeEntry();
|
||||
} catch (final Exception e) {
|
||||
// noinspection ResultOfMethodCallIgnored
|
||||
file.delete();
|
||||
outputFile.delete();
|
||||
throw e;
|
||||
}
|
||||
return file.getAbsolutePath();
|
||||
return outputFile.getFileName();
|
||||
}).whenComplete(this::exportZipComplete));
|
||||
}
|
||||
|
||||
private void exportZipComplete(@Nullable final String filePath, @Nullable final Throwable throwable) {
|
||||
if (throwable != null) {
|
||||
final String error = ExceptionLoggers.unwrapMessage(throwable);
|
||||
final String error = ErrorMessages.get(throwable);
|
||||
final String message = getContext().getString(R.string.zip_export_error, error);
|
||||
Log.e(TAG, message, throwable);
|
||||
Snackbar.make(
|
||||
@@ -108,7 +101,7 @@ public class ZipExporterPreference extends Preference {
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
return getContext().getString(R.string.zip_exporter_title);
|
||||
return getContext().getString(R.string.zip_export_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -8,7 +8,7 @@ package com.wireguard.android.util;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright © 2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.util;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.MediaStore.MediaColumns;
|
||||
|
||||
import com.wireguard.android.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class DownloadsFileSaver {
|
||||
|
||||
public static class DownloadsFile {
|
||||
private Context context;
|
||||
private OutputStream outputStream;
|
||||
private String fileName;
|
||||
private Uri uri;
|
||||
|
||||
private DownloadsFile(final Context context, final OutputStream outputStream, final String fileName, final Uri uri) {
|
||||
this.context = context;
|
||||
this.outputStream = outputStream;
|
||||
this.fileName = fileName;
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() { return outputStream; }
|
||||
public String getFileName() { return fileName; }
|
||||
|
||||
public void delete() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
context.getContentResolver().delete(uri, null, null);
|
||||
else
|
||||
new File(fileName).delete();
|
||||
}
|
||||
}
|
||||
|
||||
public static DownloadsFile save(final Context context, final String name, final String mimeType, final boolean overwriteExisting) throws Exception {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
final ContentResolver contentResolver = context.getContentResolver();
|
||||
if (overwriteExisting)
|
||||
contentResolver.delete(MediaStore.Downloads.EXTERNAL_CONTENT_URI, String.format("%s = ?", MediaColumns.DISPLAY_NAME), new String[]{name});
|
||||
final ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(MediaColumns.DISPLAY_NAME, name);
|
||||
contentValues.put(MediaColumns.MIME_TYPE, mimeType);
|
||||
final Uri contentUri = contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues);
|
||||
if (contentUri == null)
|
||||
throw new IOException(context.getString(R.string.create_downloads_file_error));
|
||||
final OutputStream contentStream = contentResolver.openOutputStream(contentUri);
|
||||
if (contentStream == null)
|
||||
throw new IOException(context.getString(R.string.create_downloads_file_error));
|
||||
Cursor cursor = contentResolver.query(contentUri, new String[]{MediaColumns.DATA}, null, null, null);
|
||||
String path = null;
|
||||
if (cursor != null) {
|
||||
try {
|
||||
if (cursor.moveToFirst())
|
||||
path = cursor.getString(0);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
if (path == null) {
|
||||
path = "Download/";
|
||||
cursor = contentResolver.query(contentUri, new String[]{MediaColumns.DISPLAY_NAME}, null, null, null);
|
||||
if (cursor != null) {
|
||||
try {
|
||||
if (cursor.moveToFirst())
|
||||
path += cursor.getString(0);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
return new DownloadsFile(context, contentStream, path, contentUri);
|
||||
} else {
|
||||
final File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
final File file = new File(path, name);
|
||||
if (!path.isDirectory() && !path.mkdirs())
|
||||
throw new IOException(context.getString(R.string.create_output_dir_error));
|
||||
return new DownloadsFile(context, new FileOutputStream(file), file.getAbsolutePath(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.util;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.config.BadConfigException;
|
||||
import com.wireguard.config.BadConfigException.Location;
|
||||
import com.wireguard.config.BadConfigException.Reason;
|
||||
import com.wireguard.config.InetEndpoint;
|
||||
import com.wireguard.config.InetNetwork;
|
||||
import com.wireguard.config.ParseException;
|
||||
import com.wireguard.crypto.Key.Format;
|
||||
import com.wireguard.crypto.KeyFormatException;
|
||||
import com.wireguard.crypto.KeyFormatException.Type;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
import java9.util.Maps;
|
||||
|
||||
public final class ErrorMessages {
|
||||
private static final Map<Reason, Integer> BCE_REASON_MAP = new EnumMap<>(Maps.of(
|
||||
Reason.INVALID_KEY, R.string.bad_config_reason_invalid_key,
|
||||
Reason.INVALID_NUMBER, R.string.bad_config_reason_invalid_number,
|
||||
Reason.INVALID_VALUE, R.string.bad_config_reason_invalid_value,
|
||||
Reason.MISSING_ATTRIBUTE, R.string.bad_config_reason_missing_attribute,
|
||||
Reason.MISSING_SECTION, R.string.bad_config_reason_missing_section,
|
||||
Reason.MISSING_VALUE, R.string.bad_config_reason_missing_value,
|
||||
Reason.SYNTAX_ERROR, R.string.bad_config_reason_syntax_error,
|
||||
Reason.UNKNOWN_ATTRIBUTE, R.string.bad_config_reason_unknown_attribute,
|
||||
Reason.UNKNOWN_SECTION, R.string.bad_config_reason_unknown_section
|
||||
));
|
||||
private static final Map<Format, Integer> KFE_FORMAT_MAP = new EnumMap<>(Maps.of(
|
||||
Format.BASE64, R.string.key_length_explanation_base64,
|
||||
Format.BINARY, R.string.key_length_explanation_binary,
|
||||
Format.HEX, R.string.key_length_explanation_hex
|
||||
));
|
||||
private static final Map<Type, Integer> KFE_TYPE_MAP = new EnumMap<>(Maps.of(
|
||||
Type.CONTENTS, R.string.key_contents_error,
|
||||
Type.LENGTH, R.string.key_length_error
|
||||
));
|
||||
private static final Map<Class, Integer> PE_CLASS_MAP = Maps.of(
|
||||
InetAddress.class, R.string.parse_error_inet_address,
|
||||
InetEndpoint.class, R.string.parse_error_inet_endpoint,
|
||||
InetNetwork.class, R.string.parse_error_inet_network,
|
||||
Integer.class, R.string.parse_error_integer
|
||||
);
|
||||
|
||||
private ErrorMessages() {
|
||||
// Prevent instantiation
|
||||
}
|
||||
|
||||
public static String get(@Nullable final Throwable throwable) {
|
||||
final Resources resources = Application.get().getResources();
|
||||
if (throwable == null)
|
||||
return resources.getString(R.string.unknown_error);
|
||||
final Throwable rootCause = rootCause(throwable);
|
||||
final String message;
|
||||
if (rootCause instanceof BadConfigException) {
|
||||
final BadConfigException bce = (BadConfigException) rootCause;
|
||||
final String reason = getBadConfigExceptionReason(resources, bce);
|
||||
final String context = bce.getLocation() == Location.TOP_LEVEL ?
|
||||
resources.getString(R.string.bad_config_context_top_level,
|
||||
bce.getSection().getName()) :
|
||||
resources.getString(R.string.bad_config_context,
|
||||
bce.getSection().getName(),
|
||||
bce.getLocation().getName());
|
||||
final String explanation = getBadConfigExceptionExplanation(resources, bce);
|
||||
message = resources.getString(R.string.bad_config_error, reason, context) + explanation;
|
||||
} else if (rootCause.getMessage() != null) {
|
||||
message = rootCause.getMessage();
|
||||
} else {
|
||||
final String errorType = rootCause.getClass().getSimpleName();
|
||||
message = resources.getString(R.string.generic_error, errorType);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private static String getBadConfigExceptionExplanation(final Resources resources,
|
||||
final BadConfigException bce) {
|
||||
if (bce.getCause() instanceof KeyFormatException) {
|
||||
final KeyFormatException kfe = (KeyFormatException) bce.getCause();
|
||||
if (kfe.getType() == Type.LENGTH)
|
||||
return resources.getString(KFE_FORMAT_MAP.get(kfe.getFormat()));
|
||||
} else if (bce.getCause() instanceof ParseException) {
|
||||
final ParseException pe = (ParseException) bce.getCause();
|
||||
if (pe.getMessage() != null)
|
||||
return ": " + pe.getMessage();
|
||||
} else if (bce.getLocation() == Location.LISTEN_PORT) {
|
||||
return resources.getString(R.string.bad_config_explanation_udp_port);
|
||||
} else if (bce.getLocation() == Location.MTU) {
|
||||
return resources.getString(R.string.bad_config_explanation_positive_number);
|
||||
} else if (bce.getLocation() == Location.PERSISTENT_KEEPALIVE) {
|
||||
return resources.getString(R.string.bad_config_explanation_pka);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static String getBadConfigExceptionReason(final Resources resources,
|
||||
final BadConfigException bce) {
|
||||
if (bce.getCause() instanceof KeyFormatException) {
|
||||
final KeyFormatException kfe = (KeyFormatException) bce.getCause();
|
||||
return resources.getString(KFE_TYPE_MAP.get(kfe.getType()));
|
||||
} else if (bce.getCause() instanceof ParseException) {
|
||||
final ParseException pe = (ParseException) bce.getCause();
|
||||
final String type = resources.getString(PE_CLASS_MAP.containsKey(pe.getParsingClass()) ?
|
||||
PE_CLASS_MAP.get(pe.getParsingClass()) : R.string.parse_error_generic);
|
||||
return resources.getString(R.string.parse_error_reason, type, pe.getText());
|
||||
}
|
||||
return resources.getString(BCE_REASON_MAP.get(bce.getReason()), bce.getText());
|
||||
}
|
||||
|
||||
private static Throwable rootCause(final Throwable throwable) {
|
||||
Throwable cause = throwable;
|
||||
while (cause.getCause() != null) {
|
||||
if (cause instanceof BadConfigException)
|
||||
break;
|
||||
cause = cause.getCause();
|
||||
}
|
||||
return cause;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,13 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.util;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.config.ParseException;
|
||||
import com.wireguard.crypto.Key;
|
||||
|
||||
import java9.util.concurrent.CompletionException;
|
||||
import java9.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
@@ -27,50 +20,12 @@ public enum ExceptionLoggers implements BiConsumer<Object, Throwable> {
|
||||
E(Log.ERROR);
|
||||
|
||||
private static final String TAG = "WireGuard/" + ExceptionLoggers.class.getSimpleName();
|
||||
|
||||
private final int priority;
|
||||
|
||||
ExceptionLoggers(final int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public static Throwable unwrap(final Throwable throwable) {
|
||||
if (throwable instanceof CompletionException && throwable.getCause() != null)
|
||||
return throwable.getCause();
|
||||
return throwable;
|
||||
}
|
||||
|
||||
public static String unwrapMessage(final Throwable throwable) {
|
||||
final Throwable innerThrowable = unwrap(throwable);
|
||||
final Resources resources = Application.get().getResources();
|
||||
String message;
|
||||
if (innerThrowable instanceof ParseException) {
|
||||
final ParseException parseException = (ParseException) innerThrowable;
|
||||
message = resources.getString(R.string.parse_error, parseException.getText(), parseException.getContext());
|
||||
if (parseException.getMessage() != null)
|
||||
message += ": " + parseException.getMessage();
|
||||
} else if (innerThrowable instanceof Key.KeyFormatException) {
|
||||
final Key.KeyFormatException keyFormatException = (Key.KeyFormatException) innerThrowable;
|
||||
switch (keyFormatException.getFormat()) {
|
||||
case BASE64:
|
||||
message = resources.getString(R.string.key_length_base64_exception_message);
|
||||
break;
|
||||
case BINARY:
|
||||
message = resources.getString(R.string.key_length_exception_message);
|
||||
break;
|
||||
case HEX:
|
||||
message = resources.getString(R.string.key_length_hex_exception_message);
|
||||
break;
|
||||
default:
|
||||
// Will never happen, as getFormat is not nullable.
|
||||
message = null;
|
||||
}
|
||||
} else {
|
||||
message = innerThrowable.getMessage();
|
||||
}
|
||||
return message != null ? message : innerThrowable.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(final Object result, @Nullable final Throwable throwable) {
|
||||
if (throwable != null)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package com.wireguard.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v7.preference.Preference;
|
||||
import androidx.preference.Preference;
|
||||
import android.view.ContextThemeWrapper;
|
||||
|
||||
import com.wireguard.android.activity.SettingsActivity;
|
||||
@@ -18,8 +18,8 @@ public final class FragmentUtils {
|
||||
public static SettingsActivity getPrefActivity(final Preference preference) {
|
||||
final Context context = preference.getContext();
|
||||
if (context instanceof ContextThemeWrapper) {
|
||||
if (((ContextThemeWrapper) context).getBaseContext() instanceof SettingsActivity) {
|
||||
return ((SettingsActivity) ((ContextThemeWrapper) context).getBaseContext());
|
||||
if (context instanceof SettingsActivity) {
|
||||
return ((SettingsActivity) context);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright © 2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
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;
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||
|
||||
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;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Signature;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
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 File moduleDir;
|
||||
private final File tmpDir;
|
||||
|
||||
public ModuleLoader(final Context context) {
|
||||
moduleDir = new File(context.getCacheDir(), "kmod");
|
||||
tmpDir = new File(context.getCacheDir(), "tmp");
|
||||
}
|
||||
|
||||
public boolean moduleMightExist() {
|
||||
return moduleDir.exists() && moduleDir.isDirectory();
|
||||
}
|
||||
|
||||
public void loadModule() throws IOException, NoRootException {
|
||||
Application.getRootShell().run(null, String.format("insmod \"%s/wireguard-$(sha256sum /proc/version|cut -d ' ' -f 1).ko\"", moduleDir.getAbsolutePath()));
|
||||
}
|
||||
|
||||
public 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Map<String, Sha256Digest> verifySignedHashes(final String signifyDigest) {
|
||||
final byte[] publicKeyBytes = Base64.decode(MODULE_PUBLIC_KEY_BASE64, Base64.DEFAULT);
|
||||
|
||||
if (publicKeyBytes == null || publicKeyBytes.length != 32 + 10 || publicKeyBytes[0] != 'E' || publicKeyBytes[1] != 'd')
|
||||
return null;
|
||||
|
||||
final String[] lines = signifyDigest.split("\n", 3);
|
||||
if (lines.length != 3)
|
||||
return null;
|
||||
if (!lines[0].startsWith("untrusted comment: "))
|
||||
return null;
|
||||
|
||||
final byte[] signatureBytes = Base64.decode(lines[1], Base64.DEFAULT);
|
||||
if (signatureBytes == null || signatureBytes.length != 64 + 10)
|
||||
return null;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
if (signatureBytes[i] != publicKeyBytes[i])
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
EdDSAParameterSpec parameterSpec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
|
||||
Signature signature = new EdDSAEngine(MessageDigest.getInstance(parameterSpec.getHashAlgorithm()));
|
||||
byte[] rawPublicKeyBytes = new byte[32];
|
||||
System.arraycopy(publicKeyBytes, 10, rawPublicKeyBytes, 0, 32);
|
||||
signature.initVerify(new EdDSAPublicKey(new EdDSAPublicKeySpec(rawPublicKeyBytes, parameterSpec)));
|
||||
signature.update(lines[2].getBytes(StandardCharsets.UTF_8));
|
||||
if (!signature.verify(signatureBytes, 10, 64))
|
||||
return null;
|
||||
} catch (final Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, Sha256Digest> hashes = new HashMap<>();
|
||||
for (final String line : lines[2].split("\n")) {
|
||||
final String[] components = line.split(" ", 2);
|
||||
if (components.length != 2)
|
||||
return null;
|
||||
try {
|
||||
hashes.put(components[1], new Sha256Digest(components[0]));
|
||||
} catch (final Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return hashes;
|
||||
}
|
||||
|
||||
public Integer download() throws IOException, NoRootException, NoSuchAlgorithmException {
|
||||
final List<String> output = new ArrayList<>();
|
||||
Application.getRootShell().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));
|
||||
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.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 OutputStream 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);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.util;
|
||||
|
||||
import android.databinding.ObservableArrayList;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.databinding.ObservableArrayList;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.wireguard.util.Keyed;
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.util;
|
||||
|
||||
import android.databinding.ObservableList;
|
||||
import androidx.databinding.ObservableList;
|
||||
|
||||
import com.wireguard.util.Keyed;
|
||||
import com.wireguard.util.KeyedList;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.util;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.wireguard.util.Keyed;
|
||||
import com.wireguard.util.SortedKeyedList;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wireguard.android.BuildConfig;
|
||||
@@ -20,7 +20,6 @@ import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@@ -44,9 +43,8 @@ public class RootShell {
|
||||
|
||||
public RootShell(final Context context) {
|
||||
deviceNotRootedMessage = context.getString(R.string.error_root);
|
||||
final File cacheDir = context.getCacheDir();
|
||||
localBinaryDir = new File(cacheDir, "bin");
|
||||
localTemporaryDir = new File(cacheDir, "tmp");
|
||||
localBinaryDir = new File(context.getCodeCacheDir(), "bin");
|
||||
localTemporaryDir = new File(context.getCacheDir(), "tmp");
|
||||
preamble = String.format("export CALLING_PACKAGE=%s PATH=\"%s:$PATH\" TMPDIR='%s'; id -u\n",
|
||||
BuildConfig.APPLICATION_ID, localBinaryDir, localTemporaryDir);
|
||||
this.context = context;
|
||||
@@ -124,10 +122,9 @@ public class RootShell {
|
||||
}
|
||||
}
|
||||
if (markersSeen != 4)
|
||||
throw new IOException(String.format(Locale.getDefault(),
|
||||
context.getResources().getString(R.string.marker_count_error), markersSeen));
|
||||
throw new IOException(context.getString(R.string.shell_marker_count_error, markersSeen));
|
||||
if (errnoStdout != errnoStderr)
|
||||
throw new IOException(context.getResources().getString(R.string.exit_status_read_error));
|
||||
throw new IOException(context.getString(R.string.shell_exit_status_read_error));
|
||||
Log.v(TAG, "exit: " + errnoStdout);
|
||||
return errnoStdout;
|
||||
}
|
||||
@@ -140,9 +137,9 @@ public class RootShell {
|
||||
if (isRunning())
|
||||
return;
|
||||
if (!localBinaryDir.isDirectory() && !localBinaryDir.mkdirs())
|
||||
throw new FileNotFoundException(context.getResources().getString(R.string.create_bin_dir_error));
|
||||
throw new FileNotFoundException(context.getString(R.string.create_bin_dir_error));
|
||||
if (!localTemporaryDir.isDirectory() && !localTemporaryDir.mkdirs())
|
||||
throw new FileNotFoundException(context.getResources().getString(R.string.create_temp_dir_error));
|
||||
throw new FileNotFoundException(context.getString(R.string.create_temp_dir_error));
|
||||
try {
|
||||
final ProcessBuilder builder = new ProcessBuilder().command(SU);
|
||||
builder.environment().put("LC_ALL", "C");
|
||||
@@ -172,8 +169,7 @@ public class RootShell {
|
||||
if (line.contains("Permission denied"))
|
||||
throw new NoRootException(deviceNotRootedMessage);
|
||||
}
|
||||
throw new IOException(String.format(Locale.getDefault(),
|
||||
context.getResources().getString(R.string.shell_start_error), process.exitValue()));
|
||||
throw new IOException(context.getString(R.string.shell_start_error, process.exitValue()));
|
||||
}
|
||||
} catch (final IOException | NoRootException e) {
|
||||
stop();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,9 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
@@ -22,6 +25,42 @@ public final class SharedLibraryLoader {
|
||||
private SharedLibraryLoader() {
|
||||
}
|
||||
|
||||
public static boolean extractLibrary(final Context context, final String libName, final File destination) throws IOException {
|
||||
final Collection<String> apks = new HashSet<>();
|
||||
if (context.getApplicationInfo().sourceDir != null)
|
||||
apks.add(context.getApplicationInfo().sourceDir);
|
||||
if (context.getApplicationInfo().splitSourceDirs != null)
|
||||
apks.addAll(Arrays.asList(context.getApplicationInfo().splitSourceDirs));
|
||||
|
||||
for (final String abi : Build.SUPPORTED_ABIS) {
|
||||
for (final String apk : apks) {
|
||||
final ZipFile zipFile;
|
||||
try {
|
||||
zipFile = new ZipFile(new File(apk), ZipFile.OPEN_READ);
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
final String mappedLibName = System.mapLibraryName(libName);
|
||||
final byte[] buffer = new byte[1024 * 32];
|
||||
final String libZipPath = "lib" + File.separatorChar + abi + File.separatorChar + mappedLibName;
|
||||
final ZipEntry zipEntry = zipFile.getEntry(libZipPath);
|
||||
if (zipEntry == null)
|
||||
continue;
|
||||
Log.d(TAG, "Extracting apk:/" + libZipPath + " to " + destination.getAbsolutePath());
|
||||
try (final FileOutputStream out = new FileOutputStream(destination);
|
||||
final InputStream in = zipFile.getInputStream(zipEntry)) {
|
||||
int len;
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, len);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void loadSharedLibrary(final Context context, final String libName) {
|
||||
Throwable noAbiException;
|
||||
try {
|
||||
@@ -31,42 +70,20 @@ public final class SharedLibraryLoader {
|
||||
Log.d(TAG, "Failed to load library normally, so attempting to extract from apk", e);
|
||||
noAbiException = e;
|
||||
}
|
||||
|
||||
final ZipFile zipFile;
|
||||
File f = null;
|
||||
try {
|
||||
zipFile = new ZipFile(new File(context.getApplicationInfo().sourceDir), ZipFile.OPEN_READ);
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
final String mappedLibName = System.mapLibraryName(libName);
|
||||
final byte[] buffer = new byte[1024 * 32];
|
||||
for (final String abi : Build.SUPPORTED_ABIS) {
|
||||
final String libZipPath = "lib" + File.separatorChar + abi + File.separatorChar + mappedLibName;
|
||||
final ZipEntry zipEntry = zipFile.getEntry(libZipPath);
|
||||
if (zipEntry == null)
|
||||
continue;
|
||||
File f = null;
|
||||
try {
|
||||
f = File.createTempFile("lib", ".so", context.getCacheDir());
|
||||
Log.d(TAG, "Extracting apk:/" + libZipPath + " to " + f.getAbsolutePath() + " and loading");
|
||||
try (final FileOutputStream out = new FileOutputStream(f);
|
||||
final InputStream in = zipFile.getInputStream(zipEntry)) {
|
||||
int len;
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, len);
|
||||
}
|
||||
}
|
||||
f = File.createTempFile("lib", ".so", context.getCodeCacheDir());
|
||||
if (extractLibrary(context, libName, f)) {
|
||||
System.load(f.getAbsolutePath());
|
||||
return;
|
||||
} catch (final Exception e) {
|
||||
Log.d(TAG, "Failed to load library apk:/" + libZipPath, e);
|
||||
noAbiException = e;
|
||||
} finally {
|
||||
if (f != null)
|
||||
// noinspection ResultOfMethodCallIgnored
|
||||
f.delete();
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
Log.d(TAG, "Failed to load library apk:/" + libName, e);
|
||||
noAbiException = e;
|
||||
} finally {
|
||||
if (f != null)
|
||||
// noinspection ResultOfMethodCallIgnored
|
||||
f.delete();
|
||||
}
|
||||
if (noAbiException instanceof RuntimeException)
|
||||
throw (RuntimeException) noAbiException;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.system.OsConstants;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -31,10 +31,7 @@ public final class ToolsInstaller {
|
||||
public static final int NO = 0x2;
|
||||
public static final int SYSTEM = 0x8;
|
||||
public static final int YES = 0x1;
|
||||
private static final String[][] EXECUTABLES = {
|
||||
{"libwg.so", "wg"},
|
||||
{"libwg-quick.so", "wg-quick"},
|
||||
};
|
||||
private static final String[] EXECUTABLES = {"wg", "wg-quick"};
|
||||
private static final File[] INSTALL_DIRS = {
|
||||
new File("/system/xbin"),
|
||||
new File("/system/bin"),
|
||||
@@ -45,13 +42,11 @@ public final class ToolsInstaller {
|
||||
private final Context context;
|
||||
private final File localBinaryDir;
|
||||
private final Object lock = new Object();
|
||||
private final File nativeLibraryDir;
|
||||
@Nullable private Boolean areToolsAvailable;
|
||||
@Nullable private Boolean installAsMagiskModule;
|
||||
|
||||
public ToolsInstaller(final Context context) {
|
||||
localBinaryDir = new File(context.getCacheDir(), "bin");
|
||||
nativeLibraryDir = new File(context.getApplicationInfo().nativeLibraryDir);
|
||||
localBinaryDir = new File(context.getCodeCacheDir(), "bin");
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@@ -72,10 +67,10 @@ public final class ToolsInstaller {
|
||||
if (INSTALL_DIR == null)
|
||||
return ERROR;
|
||||
final StringBuilder script = new StringBuilder();
|
||||
for (final String[] names : EXECUTABLES) {
|
||||
for (final String name : EXECUTABLES) {
|
||||
script.append(String.format("cmp -s '%s' '%s' && ",
|
||||
new File(nativeLibraryDir, names[0]),
|
||||
new File(INSTALL_DIR, names[1])));
|
||||
new File(localBinaryDir, name).getAbsolutePath(),
|
||||
new File(INSTALL_DIR, name).getAbsolutePath()));
|
||||
}
|
||||
script.append("exit ").append(OsConstants.EALREADY).append(';');
|
||||
try {
|
||||
@@ -89,42 +84,40 @@ public final class ToolsInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
public void ensureToolsAvailable() throws FileNotFoundException, NoRootException {
|
||||
public void ensureToolsAvailable() throws FileNotFoundException {
|
||||
synchronized (lock) {
|
||||
if (areToolsAvailable == null) {
|
||||
final int ret = symlink();
|
||||
if (ret == OsConstants.EALREADY) {
|
||||
Log.d(TAG, "Tools were already symlinked into our private binary dir");
|
||||
try {
|
||||
Log.d(TAG, extract() ? "Tools are now extracted into our private binary dir" :
|
||||
"Tools were already extracted into our private binary dir");
|
||||
areToolsAvailable = true;
|
||||
} else if (ret == OsConstants.EXIT_SUCCESS) {
|
||||
Log.d(TAG, "Tools are now symlinked into our private binary dir");
|
||||
areToolsAvailable = true;
|
||||
} else {
|
||||
Log.e(TAG, "For some reason, wg and wg-quick are not available at all");
|
||||
} catch (final IOException e) {
|
||||
Log.e(TAG, "The wg and wg-quick tools are not available", e);
|
||||
areToolsAvailable = false;
|
||||
}
|
||||
}
|
||||
if (!areToolsAvailable)
|
||||
throw new FileNotFoundException(
|
||||
context.getResources().getString(R.string.tools_unavailable_error));
|
||||
context.getString(R.string.tools_unavailable_error));
|
||||
}
|
||||
}
|
||||
|
||||
public int install() throws NoRootException {
|
||||
public int install() throws NoRootException, IOException {
|
||||
return willInstallAsMagiskModule() ? installMagisk() : installSystem();
|
||||
}
|
||||
|
||||
private int installMagisk() throws NoRootException {
|
||||
private int installMagisk() throws NoRootException, IOException {
|
||||
extract();
|
||||
final StringBuilder script = new StringBuilder("set -ex; ");
|
||||
|
||||
script.append("trap 'rm -rf /sbin/.core/img/wireguard' INT TERM EXIT; ");
|
||||
script.append(String.format("rm -rf /sbin/.core/img/wireguard/; mkdir -p /sbin/.core/img/wireguard%s; ", INSTALL_DIR));
|
||||
script.append(String.format("printf 'name=WireGuard Command Line Tools\nversion=%s\nversionCode=%s\nauthor=zx2c4\ndescription=Command line tools for WireGuard\nminMagisk=1500\n' > /sbin/.core/img/wireguard/module.prop; ", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
|
||||
script.append("touch /sbin/.core/img/wireguard/auto_mount; ");
|
||||
for (final String[] names : EXECUTABLES) {
|
||||
final File destination = new File("/sbin/.core/img/wireguard" + INSTALL_DIR, names[1]);
|
||||
script.append(String.format("cp '%s' '%s'; chmod 755 '%s'; restorecon '%s' || true; ",
|
||||
new File(nativeLibraryDir, names[0]), destination, destination, destination));
|
||||
script.append("trap 'rm -rf /sbin/.magisk/img/wireguard' INT TERM EXIT; ");
|
||||
script.append(String.format("rm -rf /sbin/.magisk/img/wireguard/; mkdir -p /sbin/.magisk/img/wireguard%s; ", INSTALL_DIR));
|
||||
script.append(String.format("printf 'name=WireGuard Command Line Tools\nversion=%s\nversionCode=%s\nauthor=zx2c4\ndescription=Command line tools for WireGuard\nminMagisk=1500\n' > /sbin/.magisk/img/wireguard/module.prop; ", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
|
||||
script.append("touch /sbin/.magisk/img/wireguard/auto_mount; ");
|
||||
for (final String name : EXECUTABLES) {
|
||||
final File destination = new File("/sbin/.magisk/img/wireguard" + INSTALL_DIR, name);
|
||||
script.append(String.format("cp '%s' '%s'; chmod 755 '%s'; chcon 'u:object_r:system_file:s0' '%s' || true; ",
|
||||
new File(localBinaryDir, name), destination, destination, destination));
|
||||
}
|
||||
script.append("trap - INT TERM EXIT;");
|
||||
|
||||
@@ -135,15 +128,16 @@ public final class ToolsInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
private int installSystem() throws NoRootException {
|
||||
private int installSystem() throws NoRootException, IOException {
|
||||
if (INSTALL_DIR == null)
|
||||
return OsConstants.ENOENT;
|
||||
extract();
|
||||
final StringBuilder script = new StringBuilder("set -ex; ");
|
||||
script.append("trap 'mount -o ro,remount /system' EXIT; mount -o rw,remount /system; ");
|
||||
for (final String[] names : EXECUTABLES) {
|
||||
final File destination = new File(INSTALL_DIR, names[1]);
|
||||
for (final String name : EXECUTABLES) {
|
||||
final File destination = new File(INSTALL_DIR, name);
|
||||
script.append(String.format("cp '%s' '%s'; chmod 755 '%s'; restorecon '%s' || true; ",
|
||||
new File(nativeLibraryDir, names[0]), destination, destination, destination));
|
||||
new File(localBinaryDir, name), destination, destination, destination));
|
||||
}
|
||||
try {
|
||||
return Application.getRootShell().run(null, script.toString()) == 0 ? YES | SYSTEM : ERROR;
|
||||
@@ -152,34 +146,30 @@ public final class ToolsInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
public int symlink() throws NoRootException {
|
||||
final StringBuilder script = new StringBuilder("set -x; ");
|
||||
for (final String[] names : EXECUTABLES) {
|
||||
script.append(String.format("test '%s' -ef '%s' && ",
|
||||
new File(nativeLibraryDir, names[0]),
|
||||
new File(localBinaryDir, names[1])));
|
||||
public boolean extract() throws IOException {
|
||||
localBinaryDir.mkdirs();
|
||||
final File files[] = new File[EXECUTABLES.length];
|
||||
boolean allExist = true;
|
||||
for (int i = 0; i < files.length; ++i) {
|
||||
files[i] = new File(localBinaryDir, EXECUTABLES[i]);
|
||||
allExist &= files[i].exists();
|
||||
}
|
||||
script.append("exit ").append(OsConstants.EALREADY).append("; set -e; ");
|
||||
|
||||
for (final String[] names : EXECUTABLES) {
|
||||
script.append(String.format("ln -fns '%s' '%s'; ",
|
||||
new File(nativeLibraryDir, names[0]),
|
||||
new File(localBinaryDir, names[1])));
|
||||
}
|
||||
script.append("exit ").append(OsConstants.EXIT_SUCCESS).append(';');
|
||||
|
||||
try {
|
||||
return Application.getRootShell().run(null, script.toString());
|
||||
} catch (final IOException ignored) {
|
||||
return OsConstants.EXIT_FAILURE;
|
||||
if (allExist)
|
||||
return false;
|
||||
for (int i = 0; i < files.length; ++i) {
|
||||
if (!SharedLibraryLoader.extractLibrary(context, EXECUTABLES[i], files[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");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean willInstallAsMagiskModule() {
|
||||
synchronized (lock) {
|
||||
if (installAsMagiskModule == null) {
|
||||
try {
|
||||
installAsMagiskModule = Application.getRootShell().run(null, "[ -d /sbin/.core/mirror -a -d /sbin/.core/img -a ! -f /cache/.disable_magisk ]") == OsConstants.EXIT_SUCCESS;
|
||||
installAsMagiskModule = Application.getRootShell().run(null, "[ -d /sbin/.magisk/mirror -a -d /sbin/.magisk/img -a ! -f /cache/.disable_magisk ]") == OsConstants.EXIT_SUCCESS;
|
||||
} catch (final Exception ignored) {
|
||||
installAsMagiskModule = false;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.viewmodel;
|
||||
|
||||
import android.databinding.ObservableArrayList;
|
||||
import android.databinding.ObservableList;
|
||||
import androidx.databinding.ObservableArrayList;
|
||||
import androidx.databinding.ObservableList;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.wireguard.config.BadConfigException;
|
||||
import com.wireguard.config.Config;
|
||||
import com.wireguard.config.ParseException;
|
||||
import com.wireguard.config.Peer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -63,7 +63,7 @@ public class ConfigProxy implements Parcelable {
|
||||
return peers;
|
||||
}
|
||||
|
||||
public Config resolve() throws ParseException {
|
||||
public Config resolve() throws BadConfigException {
|
||||
final Collection<Peer> resolvedPeers = new ArrayList<>();
|
||||
for (final PeerProxy proxy : peers)
|
||||
resolvedPeers.add(proxy.resolve());
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.viewmodel;
|
||||
|
||||
import android.databinding.BaseObservable;
|
||||
import android.databinding.Bindable;
|
||||
import android.databinding.ObservableArrayList;
|
||||
import android.databinding.ObservableList;
|
||||
import androidx.databinding.BaseObservable;
|
||||
import androidx.databinding.Bindable;
|
||||
import androidx.databinding.ObservableArrayList;
|
||||
import androidx.databinding.ObservableList;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.wireguard.android.BR;
|
||||
import com.wireguard.config.Attribute;
|
||||
import com.wireguard.config.BadConfigException;
|
||||
import com.wireguard.config.Interface;
|
||||
import com.wireguard.config.ParseException;
|
||||
import com.wireguard.crypto.Key;
|
||||
import com.wireguard.crypto.KeyFormatException;
|
||||
import com.wireguard.crypto.KeyPair;
|
||||
|
||||
import java.net.InetAddress;
|
||||
@@ -116,7 +117,7 @@ public class InterfaceProxy extends BaseObservable implements Parcelable {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public Interface resolve() throws ParseException {
|
||||
public Interface resolve() throws BadConfigException {
|
||||
final Interface.Builder builder = new Interface.Builder();
|
||||
if (!addresses.isEmpty())
|
||||
builder.parseAddresses(addresses);
|
||||
@@ -157,7 +158,7 @@ public class InterfaceProxy extends BaseObservable implements Parcelable {
|
||||
this.privateKey = privateKey;
|
||||
try {
|
||||
publicKey = new KeyPair(Key.fromBase64(privateKey)).getPublicKey().toBase64();
|
||||
} catch (final Key.KeyFormatException ignored) {
|
||||
} catch (final KeyFormatException ignored) {
|
||||
publicKey = "";
|
||||
}
|
||||
notifyPropertyChanged(BR.privateKey);
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.viewmodel;
|
||||
|
||||
import android.databinding.BaseObservable;
|
||||
import android.databinding.Bindable;
|
||||
import android.databinding.Observable;
|
||||
import android.databinding.ObservableList;
|
||||
import androidx.databinding.BaseObservable;
|
||||
import androidx.databinding.Bindable;
|
||||
import androidx.databinding.Observable;
|
||||
import androidx.databinding.ObservableList;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.wireguard.android.BR;
|
||||
import com.wireguard.config.Attribute;
|
||||
import com.wireguard.config.BadConfigException;
|
||||
import com.wireguard.config.InetEndpoint;
|
||||
import com.wireguard.config.ParseException;
|
||||
import com.wireguard.config.Peer;
|
||||
import com.wireguard.crypto.Key;
|
||||
|
||||
@@ -164,7 +164,7 @@ public class PeerProxy extends BaseObservable implements Parcelable {
|
||||
return allowedIpsState == AllowedIpsState.CONTAINS_IPV4_PUBLIC_NETWORKS;
|
||||
}
|
||||
|
||||
public Peer resolve() throws ParseException {
|
||||
public Peer resolve() throws BadConfigException {
|
||||
final Peer.Builder builder = new Peer.Builder();
|
||||
if (!allowedIps.isEmpty())
|
||||
builder.parseAllowedIPs(allowedIps);
|
||||
@@ -226,6 +226,7 @@ public class PeerProxy extends BaseObservable implements Parcelable {
|
||||
|
||||
private void setInterfaceDns(final CharSequence dnsServers) {
|
||||
final List<String> newDnsRoutes = Stream.of(Attribute.split(dnsServers))
|
||||
.filter(server -> !server.contains(":"))
|
||||
.map(server -> server + "/32")
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
if (allowedIpsState == AllowedIpsState.CONTAINS_IPV4_PUBLIC_NETWORKS) {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.widget;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.widget;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright © 2018 The Android Open Source Project
|
||||
* Copyright © 2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -23,9 +23,9 @@ import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.IntRange;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.util.FloatProperty;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright © 2013 The Android Open Source Project
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -8,7 +8,7 @@ package com.wireguard.android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.Switch;
|
||||
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -10,9 +10,9 @@ import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.support.design.widget.CoordinatorLayout;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.view.animation.FastOutSlowInInterpolator;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright © 2014 Jerzy Chalupski
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -20,11 +20,11 @@ import android.graphics.drawable.LayerDrawable;
|
||||
import android.os.Build;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.Keep;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.v4.content.res.ResourcesCompat;
|
||||
import android.support.v7.widget.AppCompatTextView;
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.TouchDelegate;
|
||||
@@ -218,7 +218,7 @@ public class FloatingActionsMenu extends ViewGroup {
|
||||
attr.recycle();
|
||||
|
||||
if (mLabelsStyle != 0 && expandsHorizontally()) {
|
||||
throw new IllegalStateException(getResources().getString(R.string.horizontal_expand_error));
|
||||
throw new IllegalStateException("Action labels in horizontal expand orientation are not supported");
|
||||
}
|
||||
|
||||
createAddButton(context);
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.widget.fab;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class FloatingActionsMenuRecyclerViewScrollListener extends RecyclerView.OnScrollListener {
|
||||
private static final float SCALE_FACTOR = 1.5f;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright © 2014 Jerzy Chalupski
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -8,8 +8,8 @@ package com.wireguard.android.widget.fab;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* Copyright © 2014 Jerzy Chalupski
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.widget.fab;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.TouchDelegate;
|
||||
import android.view.View;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.config;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.wireguard.crypto.KeyFormatException;
|
||||
|
||||
public class BadConfigException extends Exception {
|
||||
private final Location location;
|
||||
private final Reason reason;
|
||||
private final Section section;
|
||||
@Nullable private final CharSequence text;
|
||||
|
||||
private BadConfigException(final Section section, final Location location,
|
||||
final Reason reason, @Nullable final CharSequence text,
|
||||
@Nullable final Throwable cause) {
|
||||
super(cause);
|
||||
this.section = section;
|
||||
this.location = location;
|
||||
this.reason = reason;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public BadConfigException(final Section section, final Location location,
|
||||
final Reason reason, @Nullable final CharSequence text) {
|
||||
this(section, location, reason, text, null);
|
||||
}
|
||||
|
||||
public BadConfigException(final Section section, final Location location,
|
||||
final KeyFormatException cause) {
|
||||
this(section, location, Reason.INVALID_KEY, null, cause);
|
||||
}
|
||||
|
||||
public BadConfigException(final Section section, final Location location,
|
||||
@Nullable final CharSequence text,
|
||||
final NumberFormatException cause) {
|
||||
this(section, location, Reason.INVALID_NUMBER, text, cause);
|
||||
}
|
||||
|
||||
public BadConfigException(final Section section, final Location location,
|
||||
final ParseException cause) {
|
||||
this(section, location, Reason.INVALID_VALUE, cause.getText(), cause);
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public Reason getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public Section getSection() {
|
||||
return section;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CharSequence getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public enum Location {
|
||||
TOP_LEVEL(""),
|
||||
ADDRESS("Address"),
|
||||
ALLOWED_IPS("AllowedIPs"),
|
||||
DNS("DNS"),
|
||||
ENDPOINT("Endpoint"),
|
||||
EXCLUDED_APPLICATIONS("ExcludedApplications"),
|
||||
LISTEN_PORT("ListenPort"),
|
||||
MTU("MTU"),
|
||||
PERSISTENT_KEEPALIVE("PersistentKeepalive"),
|
||||
PRE_SHARED_KEY("PresharedKey"),
|
||||
PRIVATE_KEY("PrivateKey"),
|
||||
PUBLIC_KEY("PublicKey");
|
||||
|
||||
private final String name;
|
||||
|
||||
Location(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Reason {
|
||||
INVALID_KEY,
|
||||
INVALID_NUMBER,
|
||||
INVALID_VALUE,
|
||||
MISSING_ATTRIBUTE,
|
||||
MISSING_SECTION,
|
||||
MISSING_VALUE,
|
||||
SYNTAX_ERROR,
|
||||
UNKNOWN_ATTRIBUTE,
|
||||
UNKNOWN_SECTION
|
||||
}
|
||||
|
||||
public enum Section {
|
||||
CONFIG("Config"),
|
||||
INTERFACE("Interface"),
|
||||
PEER("Peer");
|
||||
|
||||
private final String name;
|
||||
|
||||
Section(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.config;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.wireguard.config.BadConfigException.Location;
|
||||
import com.wireguard.config.BadConfigException.Reason;
|
||||
import com.wireguard.config.BadConfigException.Section;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
@@ -30,32 +34,34 @@ public final class Config {
|
||||
private final List<Peer> peers;
|
||||
|
||||
private Config(final Builder builder) {
|
||||
if (builder.interfaze == null)
|
||||
throw new IllegalArgumentException("An [Interface] section is required");
|
||||
interfaze = builder.interfaze;
|
||||
interfaze = Objects.requireNonNull(builder.interfaze, "An [Interface] section is required");
|
||||
// Defensively copy to ensure immutability even if the Builder is reused.
|
||||
peers = Collections.unmodifiableList(new ArrayList<>(builder.peers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an series of "Interface" and "Peer" sections into a {@code Config}. Throws
|
||||
* {@link ParseException} if the input is not well-formed or contains unparseable sections.
|
||||
* {@link BadConfigException} if the input is not well-formed or contains data that cannot
|
||||
* be parsed.
|
||||
*
|
||||
* @param stream a stream of UTF-8 text that is interpreted as a WireGuard configuration file
|
||||
* @param stream a stream of UTF-8 text that is interpreted as a WireGuard configuration
|
||||
* @return a {@code Config} instance representing the supplied configuration
|
||||
*/
|
||||
public static Config parse(final InputStream stream) throws IOException, ParseException {
|
||||
public static Config parse(final InputStream stream)
|
||||
throws IOException, BadConfigException {
|
||||
return parse(new BufferedReader(new InputStreamReader(stream)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an series of "Interface" and "Peer" sections into a {@code Config}. Throws
|
||||
* {@link ParseException} if the input is not well-formed or contains unparseable sections.
|
||||
* {@link BadConfigException} if the input is not well-formed or contains data that cannot
|
||||
* be parsed.
|
||||
*
|
||||
* @param reader a BufferedReader of UTF-8 text that is interpreted as a WireGuard configuration file
|
||||
* @param reader a BufferedReader of UTF-8 text that is interpreted as a WireGuard configuration
|
||||
* @return a {@code Config} instance representing the supplied configuration
|
||||
*/
|
||||
public static Config parse(final BufferedReader reader) throws IOException, ParseException {
|
||||
public static Config parse(final BufferedReader reader)
|
||||
throws IOException, BadConfigException {
|
||||
final Builder builder = new Builder();
|
||||
final Collection<String> interfaceLines = new ArrayList<>();
|
||||
final Collection<String> peerLines = new ArrayList<>();
|
||||
@@ -82,20 +88,23 @@ public final class Config {
|
||||
inInterfaceSection = false;
|
||||
inPeerSection = true;
|
||||
} else {
|
||||
throw new ParseException("top level", line, "Unknown section name");
|
||||
throw new BadConfigException(Section.CONFIG, Location.TOP_LEVEL,
|
||||
Reason.UNKNOWN_SECTION, line);
|
||||
}
|
||||
} else if (inInterfaceSection) {
|
||||
interfaceLines.add(line);
|
||||
} else if (inPeerSection) {
|
||||
peerLines.add(line);
|
||||
} else {
|
||||
throw new ParseException("top level", line, "Expected [Interface] or [Peer]");
|
||||
throw new BadConfigException(Section.CONFIG, Location.TOP_LEVEL,
|
||||
Reason.UNKNOWN_SECTION, line);
|
||||
}
|
||||
}
|
||||
if (inPeerSection)
|
||||
builder.parsePeer(peerLines);
|
||||
else if (!inInterfaceSection)
|
||||
throw new ParseException("top level", "", "Empty configuration");
|
||||
throw new BadConfigException(Section.CONFIG, Location.TOP_LEVEL,
|
||||
Reason.MISSING_SECTION, null);
|
||||
// Combine all [Interface] sections in the file.
|
||||
builder.parseInterface(interfaceLines);
|
||||
return builder.build();
|
||||
@@ -189,14 +198,18 @@ public final class Config {
|
||||
}
|
||||
|
||||
public Config build() {
|
||||
if (interfaze == null)
|
||||
throw new IllegalArgumentException("An [Interface] section is required");
|
||||
return new Config(this);
|
||||
}
|
||||
|
||||
public Builder parseInterface(final Iterable<? extends CharSequence> lines) throws ParseException {
|
||||
public Builder parseInterface(final Iterable<? extends CharSequence> lines)
|
||||
throws BadConfigException {
|
||||
return setInterface(Interface.parse(lines));
|
||||
}
|
||||
|
||||
public Builder parsePeer(final Iterable<? extends CharSequence> lines) throws ParseException {
|
||||
public Builder parsePeer(final Iterable<? extends CharSequence> lines)
|
||||
throws BadConfigException {
|
||||
return addPeer(Peer.parse(lines));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.config;
|
||||
|
||||
import android.os.Build;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.Inet4Address;
|
||||
@@ -15,9 +16,12 @@ import java.net.InetAddress;
|
||||
* Utility methods for creating instances of {@link InetAddress}.
|
||||
*/
|
||||
public final class InetAddresses {
|
||||
private static final Method PARSER_METHOD;
|
||||
private static Method PARSER_METHOD;
|
||||
|
||||
static {
|
||||
|
||||
private static Method getParserMethod() {
|
||||
if (PARSER_METHOD != null)
|
||||
return PARSER_METHOD;
|
||||
try {
|
||||
// This method is only present on Android.
|
||||
// noinspection JavaReflectionMemberAccess
|
||||
@@ -25,6 +29,7 @@ public final class InetAddresses {
|
||||
} catch (final NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return PARSER_METHOD;
|
||||
}
|
||||
|
||||
private InetAddresses() {
|
||||
@@ -37,17 +42,23 @@ public final class InetAddresses {
|
||||
* @param address a string representing the IP address
|
||||
* @return an instance of {@link Inet4Address} or {@link Inet6Address}, as appropriate
|
||||
*/
|
||||
public static InetAddress parse(final String address) {
|
||||
public static InetAddress parse(final String address) throws ParseException {
|
||||
if (address.isEmpty())
|
||||
throw new IllegalArgumentException("Empty address");
|
||||
throw new ParseException(InetAddress.class, address, "Empty address");
|
||||
try {
|
||||
return (InetAddress) PARSER_METHOD.invoke(null, address);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
|
||||
return (InetAddress) getParserMethod().invoke(null, address);
|
||||
else
|
||||
return android.net.InetAddresses.parseNumericAddress(address);
|
||||
} catch (final IllegalAccessException | InvocationTargetException 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.
|
||||
throw cause instanceof IllegalArgumentException ?
|
||||
(IllegalArgumentException) cause : new RuntimeException(e);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.config;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.threeten.bp.Duration;
|
||||
import org.threeten.bp.Instant;
|
||||
@@ -26,7 +26,7 @@ import java9.util.Optional;
|
||||
* Instances of this class are externally immutable.
|
||||
*/
|
||||
public final class InetEndpoint {
|
||||
private static final Pattern BARE_IPV6 = Pattern.compile("^[^\\[]*:");
|
||||
private static final Pattern BARE_IPV6 = Pattern.compile("^[^\\[\\]]*:[^\\[\\]]*");
|
||||
private static final Pattern FORBIDDEN_CHARACTERS = Pattern.compile("[/?#]");
|
||||
|
||||
private final String host;
|
||||
@@ -42,22 +42,22 @@ public final class InetEndpoint {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public static InetEndpoint parse(final String endpoint) {
|
||||
public static InetEndpoint parse(final String endpoint) throws ParseException {
|
||||
if (FORBIDDEN_CHARACTERS.matcher(endpoint).find())
|
||||
throw new IllegalArgumentException("Forbidden characters in endpoint");
|
||||
throw new ParseException(InetEndpoint.class, endpoint, "Forbidden characters");
|
||||
final URI uri;
|
||||
try {
|
||||
uri = new URI("wg://" + endpoint);
|
||||
} catch (final URISyntaxException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
if (uri.getPort() < 0)
|
||||
throw new IllegalArgumentException("An endpoint must specify a port (e.g. 51820)");
|
||||
if (uri.getPort() < 0 || uri.getPort() > 65535)
|
||||
throw new ParseException(InetEndpoint.class, endpoint, "Missing/invalid port number");
|
||||
try {
|
||||
InetAddresses.parse(uri.getHost());
|
||||
// Parsing ths host as a numeric address worked, so we don't need to do DNS lookups.
|
||||
return new InetEndpoint(uri.getHost(), true, uri.getPort());
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
} catch (final ParseException ignored) {
|
||||
// Failed to parse the host as a numeric address, so it must be a DNS hostname/FQDN.
|
||||
return new InetEndpoint(uri.getHost(), false, uri.getPort());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -22,19 +22,28 @@ public final class InetNetwork {
|
||||
this.mask = mask;
|
||||
}
|
||||
|
||||
public static InetNetwork parse(final String network) {
|
||||
public static InetNetwork parse(final String network) throws ParseException {
|
||||
final int slash = network.lastIndexOf('/');
|
||||
final String maskString;
|
||||
final int rawMask;
|
||||
final String rawAddress;
|
||||
if (slash >= 0) {
|
||||
rawMask = Integer.parseInt(network.substring(slash + 1), 10);
|
||||
maskString = network.substring(slash + 1);
|
||||
try {
|
||||
rawMask = Integer.parseInt(maskString, 10);
|
||||
} catch (final NumberFormatException ignored) {
|
||||
throw new ParseException(Integer.class, maskString);
|
||||
}
|
||||
rawAddress = network.substring(0, slash);
|
||||
} else {
|
||||
maskString = "";
|
||||
rawMask = -1;
|
||||
rawAddress = network;
|
||||
}
|
||||
final InetAddress address = InetAddresses.parse(rawAddress);
|
||||
final int maxMask = (address instanceof Inet4Address) ? 32 : 128;
|
||||
if (rawMask > maxMask)
|
||||
throw new ParseException(InetNetwork.class, maskString, "Invalid network mask");
|
||||
final int mask = rawMask >= 0 && rawMask <= maxMask ? rawMask : maxMask;
|
||||
return new InetNetwork(address, mask);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.config;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.wireguard.config.BadConfigException.Location;
|
||||
import com.wireguard.config.BadConfigException.Reason;
|
||||
import com.wireguard.config.BadConfigException.Section;
|
||||
import com.wireguard.crypto.Key;
|
||||
import com.wireguard.crypto.KeyFormatException;
|
||||
import com.wireguard.crypto.KeyPair;
|
||||
|
||||
import java.net.InetAddress;
|
||||
@@ -22,7 +26,6 @@ import java.util.Set;
|
||||
import java9.util.Lists;
|
||||
import java9.util.Optional;
|
||||
import java9.util.stream.Collectors;
|
||||
import java9.util.stream.Stream;
|
||||
import java9.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
@@ -44,14 +47,11 @@ public final class Interface {
|
||||
private final Optional<Integer> mtu;
|
||||
|
||||
private Interface(final Builder builder) {
|
||||
if (builder.keyPair == null)
|
||||
throw new IllegalArgumentException("Interfaces must have a private key");
|
||||
|
||||
// Defensively copy to ensure immutability even if the Builder is reused.
|
||||
addresses = Collections.unmodifiableSet(new LinkedHashSet<>(builder.addresses));
|
||||
dnsServers = Collections.unmodifiableSet(new LinkedHashSet<>(builder.dnsServers));
|
||||
excludedApplications = Collections.unmodifiableSet(new LinkedHashSet<>(builder.excludedApplications));
|
||||
keyPair = builder.keyPair;
|
||||
keyPair = Objects.requireNonNull(builder.keyPair, "Interfaces must have a private key");
|
||||
listenPort = builder.listenPort;
|
||||
mtu = builder.mtu;
|
||||
}
|
||||
@@ -63,11 +63,13 @@ public final class Interface {
|
||||
* @param lines An iterable sequence of lines, containing at least a private key attribute
|
||||
* @return An {@code Interface} with all of the attributes from {@code lines} set
|
||||
*/
|
||||
public static Interface parse(final Iterable<? extends CharSequence> lines) throws ParseException {
|
||||
public static Interface parse(final Iterable<? extends CharSequence> lines)
|
||||
throws BadConfigException {
|
||||
final Builder builder = new Builder();
|
||||
for (final CharSequence line : lines) {
|
||||
final Attribute attribute = Attribute.parse(line)
|
||||
.orElseThrow(() -> new ParseException("[Interface]", line, "Syntax error"));
|
||||
final Attribute attribute = Attribute.parse(line).orElseThrow(() ->
|
||||
new BadConfigException(Section.INTERFACE, Location.TOP_LEVEL,
|
||||
Reason.SYNTAX_ERROR, line));
|
||||
switch (attribute.getKey().toLowerCase(Locale.ENGLISH)) {
|
||||
case "address":
|
||||
builder.parseAddresses(attribute.getValue());
|
||||
@@ -88,7 +90,8 @@ public final class Interface {
|
||||
builder.parsePrivateKey(attribute.getValue());
|
||||
break;
|
||||
default:
|
||||
throw new ParseException("[Interface]", attribute.getKey(), "Unknown attribute");
|
||||
throw new BadConfigException(Section.INTERFACE, Location.TOP_LEVEL,
|
||||
Reason.UNKNOWN_ATTRIBUTE, attribute.getKey());
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
@@ -263,7 +266,10 @@ public final class Interface {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Interface build() {
|
||||
public Interface build() throws BadConfigException {
|
||||
if (keyPair == null)
|
||||
throw new BadConfigException(Section.INTERFACE, Location.PRIVATE_KEY,
|
||||
Reason.MISSING_ATTRIBUTE, null);
|
||||
return new Interface(this);
|
||||
}
|
||||
|
||||
@@ -277,57 +283,51 @@ public final class Interface {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder parseAddresses(final CharSequence addresses) throws ParseException {
|
||||
public Builder parseAddresses(final CharSequence addresses) throws BadConfigException {
|
||||
try {
|
||||
final List<InetNetwork> parsed = Stream.of(Attribute.split(addresses))
|
||||
.map(InetNetwork::parse)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
return addAddresses(parsed);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
throw new ParseException("Address", addresses, e);
|
||||
for (final String address : Attribute.split(addresses))
|
||||
addAddress(InetNetwork.parse(address));
|
||||
return this;
|
||||
} catch (final ParseException e) {
|
||||
throw new BadConfigException(Section.INTERFACE, Location.ADDRESS, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Builder parseDnsServers(final CharSequence dnsServers) throws ParseException {
|
||||
public Builder parseDnsServers(final CharSequence dnsServers) throws BadConfigException {
|
||||
try {
|
||||
final List<InetAddress> parsed = Stream.of(Attribute.split(dnsServers))
|
||||
.map(InetAddresses::parse)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
return addDnsServers(parsed);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
throw new ParseException("DNS", dnsServers, e);
|
||||
for (final String dnsServer : Attribute.split(dnsServers))
|
||||
addDnsServer(InetAddresses.parse(dnsServer));
|
||||
return this;
|
||||
} catch (final ParseException e) {
|
||||
throw new BadConfigException(Section.INTERFACE, Location.DNS, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Builder parseExcludedApplications(final CharSequence apps) throws ParseException {
|
||||
try {
|
||||
return excludeApplications(Lists.of(Attribute.split(apps)));
|
||||
} catch (final IllegalArgumentException e) {
|
||||
throw new ParseException("ExcludedApplications", apps, e);
|
||||
}
|
||||
public Builder parseExcludedApplications(final CharSequence apps) {
|
||||
return excludeApplications(Lists.of(Attribute.split(apps)));
|
||||
}
|
||||
|
||||
public Builder parseListenPort(final String listenPort) throws ParseException {
|
||||
public Builder parseListenPort(final String listenPort) throws BadConfigException {
|
||||
try {
|
||||
return setListenPort(Integer.parseInt(listenPort));
|
||||
} catch (final IllegalArgumentException e) {
|
||||
throw new ParseException("ListenPort", listenPort, e);
|
||||
} catch (final NumberFormatException e) {
|
||||
throw new BadConfigException(Section.INTERFACE, Location.LISTEN_PORT, listenPort, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Builder parseMtu(final String mtu) throws ParseException {
|
||||
public Builder parseMtu(final String mtu) throws BadConfigException {
|
||||
try {
|
||||
return setMtu(Integer.parseInt(mtu));
|
||||
} catch (final IllegalArgumentException e) {
|
||||
throw new ParseException("MTU", mtu, e);
|
||||
} catch (final NumberFormatException e) {
|
||||
throw new BadConfigException(Section.INTERFACE, Location.MTU, mtu, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Builder parsePrivateKey(final String privateKey) throws ParseException {
|
||||
public Builder parsePrivateKey(final String privateKey) throws BadConfigException {
|
||||
try {
|
||||
return setKeyPair(new KeyPair(Key.fromBase64(privateKey)));
|
||||
} catch (final Key.KeyFormatException e) {
|
||||
throw new ParseException("PrivateKey", "(omitted)", e);
|
||||
} catch (final KeyFormatException e) {
|
||||
throw new BadConfigException(Section.INTERFACE, Location.PRIVATE_KEY, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,16 +336,18 @@ public final class Interface {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setListenPort(final int listenPort) {
|
||||
public Builder setListenPort(final int listenPort) throws BadConfigException {
|
||||
if (listenPort < MIN_UDP_PORT || listenPort > MAX_UDP_PORT)
|
||||
throw new IllegalArgumentException("ListenPort must be a valid UDP port number");
|
||||
throw new BadConfigException(Section.INTERFACE, Location.LISTEN_PORT,
|
||||
Reason.INVALID_VALUE, String.valueOf(listenPort));
|
||||
this.listenPort = listenPort == 0 ? Optional.empty() : Optional.of(listenPort);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMtu(final int mtu) {
|
||||
public Builder setMtu(final int mtu) throws BadConfigException {
|
||||
if (mtu < 0)
|
||||
throw new IllegalArgumentException("MTU must not be negative");
|
||||
throw new BadConfigException(Section.INTERFACE, Location.LISTEN_PORT,
|
||||
Reason.INVALID_VALUE, String.valueOf(mtu));
|
||||
this.mtu = mtu == 0 ? Optional.empty() : Optional.of(mtu);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1,38 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.config;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An exception representing a failure to parse an element of a WireGuard configuration. The context
|
||||
* for this failure can be retrieved with {@link #getContext}, and the text that failed to parse can
|
||||
* be retrieved with {@link #getText}.
|
||||
*/
|
||||
public class ParseException extends Exception {
|
||||
private final String context;
|
||||
private final Class<?> parsingClass;
|
||||
private final CharSequence text;
|
||||
|
||||
public ParseException(final String context, final CharSequence text, final String message) {
|
||||
super(message);
|
||||
this.context = context;
|
||||
public ParseException(final Class<?> parsingClass, final CharSequence text,
|
||||
@Nullable final String message, @Nullable final Throwable cause) {
|
||||
super(message, cause);
|
||||
this.parsingClass = parsingClass;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public ParseException(final String context, final CharSequence text, final Throwable cause) {
|
||||
super(cause.getMessage(), cause);
|
||||
this.context = context;
|
||||
this.text = text;
|
||||
public ParseException(final Class<?> parsingClass, final CharSequence text,
|
||||
@Nullable final String message) {
|
||||
this(parsingClass, text, message, null);
|
||||
}
|
||||
|
||||
public ParseException(final String context, final CharSequence text) {
|
||||
this.context = context;
|
||||
this.text = text;
|
||||
public ParseException(final Class<?> parsingClass, final CharSequence text,
|
||||
@Nullable final Throwable cause) {
|
||||
this(parsingClass, text, null, cause);
|
||||
}
|
||||
|
||||
public String getContext() {
|
||||
return context;
|
||||
public ParseException(final Class<?> parsingClass, final CharSequence text) {
|
||||
this(parsingClass, text, null, null);
|
||||
}
|
||||
|
||||
public Class<?> getParsingClass() {
|
||||
return parsingClass;
|
||||
}
|
||||
|
||||
public CharSequence getText() {
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.config;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.wireguard.config.BadConfigException.Location;
|
||||
import com.wireguard.config.BadConfigException.Reason;
|
||||
import com.wireguard.config.BadConfigException.Section;
|
||||
import com.wireguard.crypto.Key;
|
||||
import com.wireguard.crypto.KeyFormatException;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import java9.util.Optional;
|
||||
import java9.util.stream.Collectors;
|
||||
import java9.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Represents the configuration for a WireGuard peer (a [Peer] block). Peers must have a public key,
|
||||
@@ -35,14 +36,12 @@ public final class Peer {
|
||||
private final Key publicKey;
|
||||
|
||||
private Peer(final Builder builder) {
|
||||
if (builder.publicKey == null)
|
||||
throw new IllegalArgumentException("Peers must have a public key");
|
||||
// Defensively copy to ensure immutability even if the Builder is reused.
|
||||
allowedIps = Collections.unmodifiableSet(new LinkedHashSet<>(builder.allowedIps));
|
||||
endpoint = builder.endpoint;
|
||||
persistentKeepalive = builder.persistentKeepalive;
|
||||
preSharedKey = builder.preSharedKey;
|
||||
publicKey = builder.publicKey;
|
||||
publicKey = Objects.requireNonNull(builder.publicKey, "Peers must have a public key");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,11 +51,13 @@ public final class Peer {
|
||||
* @param lines an iterable sequence of lines, containing at least a public key attribute
|
||||
* @return a {@code Peer} with all of its attributes set from {@code lines}
|
||||
*/
|
||||
public static Peer parse(final Iterable<? extends CharSequence> lines) throws ParseException {
|
||||
public static Peer parse(final Iterable<? extends CharSequence> lines)
|
||||
throws BadConfigException {
|
||||
final Builder builder = new Builder();
|
||||
for (final CharSequence line : lines) {
|
||||
final Attribute attribute = Attribute.parse(line)
|
||||
.orElseThrow(() -> new ParseException("[Peer]", line, "Syntax error"));
|
||||
final Attribute attribute = Attribute.parse(line).orElseThrow(() ->
|
||||
new BadConfigException(Section.PEER, Location.TOP_LEVEL,
|
||||
Reason.SYNTAX_ERROR, line));
|
||||
switch (attribute.getKey().toLowerCase(Locale.ENGLISH)) {
|
||||
case "allowedips":
|
||||
builder.parseAllowedIPs(attribute.getValue());
|
||||
@@ -74,7 +75,8 @@ public final class Peer {
|
||||
builder.parsePublicKey(attribute.getValue());
|
||||
break;
|
||||
default:
|
||||
throw new ParseException("[Peer]", line, "Unknown attribute");
|
||||
throw new BadConfigException(Section.PEER, Location.TOP_LEVEL,
|
||||
Reason.UNKNOWN_ATTRIBUTE, attribute.getKey());
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
@@ -225,50 +227,54 @@ public final class Peer {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Peer build() {
|
||||
public Peer build() throws BadConfigException {
|
||||
if (publicKey == null)
|
||||
throw new BadConfigException(Section.PEER, Location.PUBLIC_KEY,
|
||||
Reason.MISSING_ATTRIBUTE, null);
|
||||
return new Peer(this);
|
||||
}
|
||||
|
||||
public Builder parseAllowedIPs(final CharSequence allowedIps) throws ParseException {
|
||||
public Builder parseAllowedIPs(final CharSequence allowedIps) throws BadConfigException {
|
||||
try {
|
||||
final List<InetNetwork> parsed = Stream.of(Attribute.split(allowedIps))
|
||||
.map(InetNetwork::parse)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
return addAllowedIps(parsed);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
throw new ParseException("AllowedIPs", allowedIps, e);
|
||||
for (final String allowedIp : Attribute.split(allowedIps))
|
||||
addAllowedIp(InetNetwork.parse(allowedIp));
|
||||
return this;
|
||||
} catch (final ParseException e) {
|
||||
throw new BadConfigException(Section.PEER, Location.ALLOWED_IPS, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Builder parseEndpoint(final String endpoint) throws ParseException {
|
||||
public Builder parseEndpoint(final String endpoint) throws BadConfigException {
|
||||
try {
|
||||
return setEndpoint(InetEndpoint.parse(endpoint));
|
||||
} catch (final IllegalArgumentException e) {
|
||||
throw new ParseException("Endpoint", endpoint, e);
|
||||
} catch (final ParseException e) {
|
||||
throw new BadConfigException(Section.PEER, Location.ENDPOINT, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Builder parsePersistentKeepalive(final String persistentKeepalive) throws ParseException {
|
||||
public Builder parsePersistentKeepalive(final String persistentKeepalive)
|
||||
throws BadConfigException {
|
||||
try {
|
||||
return setPersistentKeepalive(Integer.parseInt(persistentKeepalive));
|
||||
} catch (final IllegalArgumentException e) {
|
||||
throw new ParseException("PersistentKeepalive", persistentKeepalive, e);
|
||||
} catch (final NumberFormatException e) {
|
||||
throw new BadConfigException(Section.PEER, Location.PERSISTENT_KEEPALIVE,
|
||||
persistentKeepalive, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Builder parsePreSharedKey(final String preSharedKey) throws ParseException {
|
||||
public Builder parsePreSharedKey(final String preSharedKey) throws BadConfigException {
|
||||
try {
|
||||
return setPreSharedKey(Key.fromBase64(preSharedKey));
|
||||
} catch (final Key.KeyFormatException e) {
|
||||
throw new ParseException("PresharedKey", preSharedKey, e);
|
||||
} catch (final KeyFormatException e) {
|
||||
throw new BadConfigException(Section.PEER, Location.PRE_SHARED_KEY, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Builder parsePublicKey(final String publicKey) throws ParseException {
|
||||
public Builder parsePublicKey(final String publicKey) throws BadConfigException {
|
||||
try {
|
||||
return setPublicKey(Key.fromBase64(publicKey));
|
||||
} catch (final Key.KeyFormatException e) {
|
||||
throw new ParseException("PublicKey", publicKey, e);
|
||||
} catch (final KeyFormatException e) {
|
||||
throw new BadConfigException(Section.PEER, Location.PUBLIC_KEY, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,9 +283,11 @@ public final class Peer {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPersistentKeepalive(final int persistentKeepalive) {
|
||||
public Builder setPersistentKeepalive(final int persistentKeepalive)
|
||||
throws BadConfigException {
|
||||
if (persistentKeepalive < 0 || persistentKeepalive > MAX_PERSISTENT_KEEPALIVE)
|
||||
throw new IllegalArgumentException("Invalid value for PersistentKeepalive");
|
||||
throw new BadConfigException(Section.PEER, Location.PERSISTENT_KEEPALIVE,
|
||||
Reason.INVALID_VALUE, String.valueOf(persistentKeepalive));
|
||||
this.persistentKeepalive = persistentKeepalive == 0 ?
|
||||
Optional.empty() : Optional.of(persistentKeepalive);
|
||||
return this;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright © 2016 Southern Storm Software, Pty Ltd.
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.crypto;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.crypto;
|
||||
|
||||
import com.wireguard.crypto.KeyFormatException.Type;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
@@ -83,10 +86,10 @@ public final class Key {
|
||||
* @param str the base64 string representation of a WireGuard key
|
||||
* @return the decoded key encapsulated in an immutable container
|
||||
*/
|
||||
public static Key fromBase64(final String str) {
|
||||
public static Key fromBase64(final String str) throws KeyFormatException {
|
||||
final char[] input = str.toCharArray();
|
||||
if (input.length != Format.BASE64.length || input[Format.BASE64.length - 1] != '=')
|
||||
throw new KeyFormatException(Format.BASE64);
|
||||
throw new KeyFormatException(Format.BASE64, Type.LENGTH);
|
||||
final byte[] key = new byte[Format.BINARY.length];
|
||||
int i;
|
||||
int ret = 0;
|
||||
@@ -109,7 +112,7 @@ public final class Key {
|
||||
key[i * 3 + 1] = (byte) ((val >>> 8) & 0xff);
|
||||
|
||||
if (ret != 0)
|
||||
throw new KeyFormatException(Format.BASE64);
|
||||
throw new KeyFormatException(Format.BASE64, Type.CONTENTS);
|
||||
return new Key(key);
|
||||
}
|
||||
|
||||
@@ -120,9 +123,9 @@ public final class Key {
|
||||
* @param bytes an array of bytes containing a WireGuard key in binary format
|
||||
* @return the key encapsulated in an immutable container
|
||||
*/
|
||||
public static Key fromBytes(final byte[] bytes) {
|
||||
public static Key fromBytes(final byte[] bytes) throws KeyFormatException {
|
||||
if (bytes.length != Format.BINARY.length)
|
||||
throw new KeyFormatException(Format.BINARY);
|
||||
throw new KeyFormatException(Format.BINARY, Type.LENGTH);
|
||||
return new Key(bytes);
|
||||
}
|
||||
|
||||
@@ -133,10 +136,10 @@ public final class Key {
|
||||
* @param str the hexadecimal string representation of a WireGuard key
|
||||
* @return the decoded key encapsulated in an immutable container
|
||||
*/
|
||||
public static Key fromHex(final String str) {
|
||||
public static Key fromHex(final String str) throws KeyFormatException {
|
||||
final char[] input = str.toCharArray();
|
||||
if (input.length != Format.HEX.length)
|
||||
throw new KeyFormatException(Format.HEX);
|
||||
throw new KeyFormatException(Format.HEX, Type.LENGTH);
|
||||
final byte[] key = new byte[Format.BINARY.length];
|
||||
int ret = 0;
|
||||
for (int i = 0; i < key.length; ++i) {
|
||||
@@ -167,10 +170,37 @@ public final class Key {
|
||||
key[i] = (byte) (cAcc | cVal);
|
||||
}
|
||||
if (ret != 0)
|
||||
throw new KeyFormatException(Format.HEX);
|
||||
throw new KeyFormatException(Format.HEX, Type.CONTENTS);
|
||||
return new Key(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a private key using the system's {@link SecureRandom} number generator.
|
||||
*
|
||||
* @return a well-formed random private key
|
||||
*/
|
||||
static Key generatePrivateKey() {
|
||||
final SecureRandom secureRandom = new SecureRandom();
|
||||
final byte[] privateKey = new byte[Format.BINARY.getLength()];
|
||||
secureRandom.nextBytes(privateKey);
|
||||
privateKey[0] &= 248;
|
||||
privateKey[31] &= 127;
|
||||
privateKey[31] |= 64;
|
||||
return new Key(privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a public key from an existing private key.
|
||||
*
|
||||
* @param privateKey a private key
|
||||
* @return a well-formed public key that corresponds to the supplied private key
|
||||
*/
|
||||
static Key generatePublicKey(final Key privateKey) {
|
||||
final byte[] publicKey = new byte[Format.BINARY.getLength()];
|
||||
Curve25519.eval(publicKey, 0, privateKey.getBytes(), null);
|
||||
return new Key(publicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key as an array of bytes.
|
||||
*
|
||||
@@ -236,20 +266,4 @@ public final class Key {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An exception thrown when attempting to parse an invalid key (too short, too long, or byte
|
||||
* data inappropriate for the format). The format being parsed can be accessed with the
|
||||
* {@link #getFormat} method.
|
||||
*/
|
||||
public static final class KeyFormatException extends RuntimeException {
|
||||
private final Format format;
|
||||
|
||||
private KeyFormatException(final Format format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public Format getFormat() {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.crypto;
|
||||
|
||||
/**
|
||||
* An exception thrown when attempting to parse an invalid key (too short, too long, or byte
|
||||
* data inappropriate for the format). The format being parsed can be accessed with the
|
||||
* {@link #getFormat} method.
|
||||
*/
|
||||
public final class KeyFormatException extends Exception {
|
||||
private final Key.Format format;
|
||||
private final Type type;
|
||||
|
||||
KeyFormatException(final Key.Format format, final Type type) {
|
||||
this.format = format;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Key.Format getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
CONTENTS,
|
||||
LENGTH
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.crypto;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* Represents a Curve25519 key pair as used by WireGuard.
|
||||
* <p>
|
||||
@@ -20,7 +18,7 @@ public class KeyPair {
|
||||
* Creates a key pair using a newly-generated private key.
|
||||
*/
|
||||
public KeyPair() {
|
||||
this(generatePrivateKey());
|
||||
this(Key.generatePrivateKey());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,35 +28,7 @@ public class KeyPair {
|
||||
*/
|
||||
public KeyPair(final Key privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
publicKey = generatePublicKey(privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a private key using the system's {@link SecureRandom} number generator.
|
||||
*
|
||||
* @return a well-formed random private key
|
||||
*/
|
||||
@SuppressWarnings("MagicNumber")
|
||||
private static Key generatePrivateKey() {
|
||||
final SecureRandom secureRandom = new SecureRandom();
|
||||
final byte[] privateKey = new byte[Key.Format.BINARY.getLength()];
|
||||
secureRandom.nextBytes(privateKey);
|
||||
privateKey[0] &= 248;
|
||||
privateKey[31] &= 127;
|
||||
privateKey[31] |= 64;
|
||||
return Key.fromBytes(privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a public key from an existing private key.
|
||||
*
|
||||
* @param privateKey a private key
|
||||
* @return a well-formed public key that corresponds to the supplied private key
|
||||
*/
|
||||
private static Key generatePublicKey(final Key privateKey) {
|
||||
final byte[] publicKey = new byte[Key.Format.BINARY.getLength()];
|
||||
Curve25519.eval(publicKey, 0, privateKey.getBytes(), null);
|
||||
return Key.fromBytes(publicKey);
|
||||
publicKey = Key.generatePublicKey(privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.util;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.util;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
android:indeterminate="true"
|
||||
android:visibility="@{appData.isEmpty() ? View.VISIBLE : View.GONE}" />
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/app_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/tunnel_name_text_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
@@ -25,7 +25,7 @@
|
||||
android:inputType="textNoSuggestions|textVisiblePassword"
|
||||
app:filter="@{NameInputFilter.newInstance()}" />
|
||||
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
@@ -122,7 +122,7 @@
|
||||
android:contentDescription="@string/addresses"
|
||||
android:text="@{config.interface.addresses}" />
|
||||
</RelativeLayout>
|
||||
</android.support.v7.widget.CardView>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
type="com.wireguard.config.Peer" />
|
||||
</data>
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
@@ -90,5 +90,5 @@
|
||||
android:layout_below="@+id/endpoint_label"
|
||||
android:text="@{item.endpoint}" />
|
||||
</RelativeLayout>
|
||||
</android.support.v7.widget.CardView>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</layout>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
type="String" />
|
||||
</data>
|
||||
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/main_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -39,7 +39,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
@@ -228,7 +228,7 @@
|
||||
android:onClick="@{fragment::onRequestSetExcludedApplications}"
|
||||
android:text="@{@plurals/set_excluded_applications(config.interface.excludedApplications.size, config.interface.excludedApplications.size)}" />
|
||||
</RelativeLayout>
|
||||
</android.support.v7.widget.CardView>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -250,5 +250,5 @@
|
||||
android:text="@string/add_peer" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</layout>
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
|
||||
<variable
|
||||
name="collection"
|
||||
type="android.databinding.ObservableList<com.wireguard.android.viewmodel.PeerProxy>" />
|
||||
type="androidx.databinding.ObservableList<com.wireguard.android.viewmodel.PeerProxy>" />
|
||||
|
||||
<variable
|
||||
name="item"
|
||||
type="com.wireguard.android.viewmodel.PeerProxy" />
|
||||
</data>
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
@@ -157,5 +157,5 @@
|
||||
android:text="@={item.persistentKeepalive}"
|
||||
android:textAlignment="center" />
|
||||
</RelativeLayout>
|
||||
</android.support.v7.widget.CardView>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</layout>
|
||||
|
||||
@@ -19,14 +19,14 @@
|
||||
type="com.wireguard.android.util.ObservableKeyedList<String, Tunnel>" />
|
||||
</data>
|
||||
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/main_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/colorBackground"
|
||||
android:clipChildren="false">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/tunnel_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -45,7 +45,7 @@
|
||||
android:orientation="vertical"
|
||||
android:visibility="@{tunnels.size() == 0 ? android.view.View.VISIBLE : android.view.View.GONE}">
|
||||
|
||||
<android.support.v7.widget.AppCompatImageView
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/logo_placeholder"
|
||||
android:layout_width="140dp"
|
||||
android:layout_height="140dp"
|
||||
@@ -89,7 +89,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{fragment::onRequestScanQRCode}"
|
||||
app:fabSize="mini"
|
||||
app:fab_title="@string/create_from_qrcode"
|
||||
app:fab_title="@string/create_from_qr_code"
|
||||
app:srcCompat="@drawable/ic_action_scan_qr_code_white" />
|
||||
|
||||
<com.wireguard.android.widget.fab.LabeledFloatingActionButton
|
||||
@@ -101,5 +101,5 @@
|
||||
app:fab_title="@string/create_empty"
|
||||
app:srcCompat="@drawable/ic_action_edit_white" />
|
||||
</com.wireguard.android.widget.fab.FloatingActionsMenu>
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</layout>
|
||||
|
||||
@@ -26,10 +26,13 @@
|
||||
</data>
|
||||
|
||||
<com.wireguard.android.widget.MultiselectableRelativeLayout
|
||||
android:id="@+id/tunnel_list_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/list_item_background"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:descendantFocusability="beforeDescendants"
|
||||
android:focusable="true"
|
||||
android:nextFocusRight="@+id/tunnel_switch"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
@@ -49,6 +52,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBaseline="@+id/tunnel_name"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:nextFocusLeft="@+id/tunnel_list_item"
|
||||
app:checked="@{item.state == State.UP}"
|
||||
app:onBeforeCheckedChanged="@{fragment::setTunnelState}" />
|
||||
</com.wireguard.android.widget.MultiselectableRelativeLayout>
|
||||
|
||||
@@ -28,28 +28,47 @@
|
||||
<string name="addresses">Addresses</string>
|
||||
<string name="allowed_ips">Allowed IPs</string>
|
||||
<string name="app_name">WireGuard</string>
|
||||
<string name="bad_config_context">%1$s\'s %2$s</string>
|
||||
<string name="bad_config_context_top_level">%s</string>
|
||||
<string name="bad_config_error">%1$s in %2$s</string>
|
||||
<string name="bad_config_explanation_pka">: Must be positive and no more than 65535</string>
|
||||
<string name="bad_config_explanation_positive_number">: Must be positive</string>
|
||||
<string name="bad_config_explanation_udp_port">: Must be a valid UDP port number</string>
|
||||
<string name="bad_config_reason_invalid_key">Invalid key</string>
|
||||
<string name="bad_config_reason_invalid_number">Invalid number</string>
|
||||
<string name="bad_config_reason_invalid_value">Invalid value</string>
|
||||
<string name="bad_config_reason_missing_attribute">Missing attribute</string>
|
||||
<string name="bad_config_reason_missing_section">Missing section</string>
|
||||
<string name="bad_config_reason_missing_value">Missing value</string>
|
||||
<string name="bad_config_reason_syntax_error">Syntax error</string>
|
||||
<string name="bad_config_reason_unknown_attribute">Unknown attribute</string>
|
||||
<string name="bad_config_reason_unknown_section">Unknown section</string>
|
||||
<string name="bad_config_reason_value_out_of_range">Value out of range</string>
|
||||
<string name="bad_extension_error">File must be .conf or .zip</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="config_delete_error">Cannot delete configuration file %s</string>
|
||||
<string name="config_exists_error">Configuration for %s already exists</string>
|
||||
<string name="config_file_exists_error">Configuration file %s already exists</string>
|
||||
<string name="config_not_found_error">Configuration file %s not found</string>
|
||||
<string name="config_rename_error">Cannot rename configuration file %s</string>
|
||||
<string name="config_save_error">Unable to save configuration for “%1$s”: %2$s</string>
|
||||
<string name="config_exists_error">Configuration for “%s” already exists</string>
|
||||
<string name="config_file_exists_error">Configuration file “%s” already exists</string>
|
||||
<string name="config_not_found_error">Configuration file “%s” not found</string>
|
||||
<string name="config_rename_error">Cannot rename configuration file “%s”</string>
|
||||
<string name="config_save_error">Cannot save configuration for “%1$s”: %2$s</string>
|
||||
<string name="config_save_success">Successfully saved configuration for “%s”</string>
|
||||
<string name="create_activity_title">Create WireGuard Tunnel</string>
|
||||
<string name="create_bin_dir_error">Could not create local binary directory</string>
|
||||
<string name="create_bin_dir_error">Cannot create local binary directory</string>
|
||||
<string name="create_empty">Create from scratch</string>
|
||||
<string name="create_from_file">Create from file or archive</string>
|
||||
<string name="create_from_qrcode">Create from QR code</string>
|
||||
<string name="create_from_qr_code">Create from QR code</string>
|
||||
<string name="create_output_dir_error">Cannot create output directory</string>
|
||||
<string name="create_temp_dir_error">Could not create local temporary directory</string>
|
||||
<string name="create_downloads_file_error">Cannot create file in downloads directory</string>
|
||||
<string name="create_temp_dir_error">Cannot create local temporary directory</string>
|
||||
<string name="create_tunnel">Create Tunnel</string>
|
||||
<string name="dark_theme_summary_off">Currently using light day theme</string>
|
||||
<string name="dark_theme_summary_on">Currently using dark night theme</string>
|
||||
<string name="dark_theme_summary_off">Currently using light (day) theme</string>
|
||||
<string name="dark_theme_summary_on">Currently using dark (night) theme</string>
|
||||
<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>
|
||||
@@ -59,36 +78,45 @@
|
||||
<string name="error_up">Error bringing up tunnel: %s</string>
|
||||
<string name="exclude_private_ips">Exclude private IPs</string>
|
||||
<string name="excluded_applications">Excluded Applications</string>
|
||||
<string name="exit_status_read_error">Unable to read exit status</string>
|
||||
<string name="generate">Generate</string>
|
||||
<string name="generic_error">Unknown “%s” error</string>
|
||||
<string name="hint_automatic">(auto)</string>
|
||||
<string name="hint_generated">(generated)</string>
|
||||
<string name="hint_optional">(optional)</string>
|
||||
<string name="hint_random">(random)</string>
|
||||
<string name="horizontal_expand_error">Action labels in horizontal expand orientation is not supported.</string>
|
||||
<string name="illegal_filename_error">Illegal file name %s</string>
|
||||
<string name="illegal_filename_error">Illegal file name “%s”</string>
|
||||
<string name="import_error">Unable to import tunnel: %s</string>
|
||||
<string name="import_from_qrcode">Import Tunnel from QR Code</string>
|
||||
<string name="import_from_qr_code">Import Tunnel from QR Code</string>
|
||||
<string name="import_success">Imported “%s”</string>
|
||||
<string name="interface_title">Interface</string>
|
||||
<string name="invalid_config_error">Invalid config passed to %s</string>
|
||||
<string name="key_length_base64_exception_message">WireGuard base64 keys must be 44 characters encoding 32 bytes</string>
|
||||
<string name="key_length_exception_message">WireGuard keys must be 32 bytes</string>
|
||||
<string name="key_length_hex_exception_message">WireGuard hex keys must be 64 characters encoding 32 bytes</string>
|
||||
<string name="key_length_explanation_base64">: WireGuard base64 keys must be 44 characters (32 bytes)</string>
|
||||
<string name="key_length_explanation_binary">: WireGuard keys must be 32 bytes</string>
|
||||
<string name="key_length_explanation_hex">: WireGuard hex keys must be 64 characters (32 bytes)</string>
|
||||
<string name="listen_port">Listen port</string>
|
||||
<string name="log_export_error">Unable to export log: %s</string>
|
||||
<string name="log_export_success">Saved to %s</string>
|
||||
<string name="log_export_success">Saved to “%s”</string>
|
||||
<string name="log_export_summary">Log file will be saved to downloads folder</string>
|
||||
<string name="log_exporter_title">Export log file</string>
|
||||
<string name="marker_count_error">Expected 4 markers, received %i</string>
|
||||
<string name="log_export_title">Export log file</string>
|
||||
<string name="logcat_error">Unable to run logcat: </string>
|
||||
<string name="module_version_error">Unable to determine kernel module version</string>
|
||||
<string name="module_installer_not_found">No modules are available for your device</string>
|
||||
<string name="module_installer_initial">The experimental kernel module can improve performance</string>
|
||||
<string name="module_installer_success">Success. The application will restart in 5 seconds</string>
|
||||
<string name="module_installer_title">Download and install kernel module</string>
|
||||
<string name="module_installer_working">Downloading and installing…</string>
|
||||
<string name="module_installer_error">Something went wrong. Please try again</string>
|
||||
<string name="mtu">MTU</string>
|
||||
<string name="multiple_tunnels_error">Only one userspace tunnel can run at a time</string>
|
||||
<string name="name">Name</string>
|
||||
<string name="no_config_error">Trying to bring up a tunnel with no config</string>
|
||||
<string name="no_configs_error">No configurations found</string>
|
||||
<string name="no_tunnels_error">No tunnels exist</string>
|
||||
<string name="parse_error">Cannot parse “%1$s” at %2$s</string>
|
||||
<string name="parse_error_generic">string</string>
|
||||
<string name="parse_error_inet_address">IP address</string>
|
||||
<string name="parse_error_inet_endpoint">endpoint</string>
|
||||
<string name="parse_error_inet_network">IP network</string>
|
||||
<string name="parse_error_integer">number</string>
|
||||
<string name="parse_error_reason">Cannot parse %1$s “%2$s”</string>
|
||||
<string name="peer">Peer</string>
|
||||
<string name="permission_description">Allows an app to control WireGuard tunnels. Apps with this permission may enable and disable WireGuard tunnels at will, potentially misdirecting Internet traffic.</string>
|
||||
<string name="permission_label">control WireGuard tunnels</string>
|
||||
@@ -97,14 +125,16 @@
|
||||
<string name="private_key">Private key</string>
|
||||
<string name="public_key">Public key</string>
|
||||
<string name="public_key_description">Public key</string>
|
||||
<string name="qrcode_hint">Tip: generate with `qrencode -t ansiutf8 < tunnel.conf`.</string>
|
||||
<string name="qr_code_hint">Tip: generate with `qrencode -t ansiutf8 < tunnel.conf`.</string>
|
||||
<string name="restore_on_boot_summary">Bring up previously-enabled tunnels on boot</string>
|
||||
<string name="restore_on_boot_title">Restore on boot</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="select_all">Select all</string>
|
||||
<string name="set_exclusions">Set Exclusions</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="shell_start_error">Shell failed to start: %i</string>
|
||||
<string name="shell_exit_status_read_error">Shell cannot read exit status</string>
|
||||
<string name="shell_marker_count_error">Shell expected 4 markers, received %d</string>
|
||||
<string name="shell_start_error">Shell failed to start: %d</string>
|
||||
<string name="toggle_error">Error toggling WireGuard tunnel: %s</string>
|
||||
<string name="tools_installer_already">wg and wg-quick are already installed</string>
|
||||
<string name="tools_installer_failure">Unable to install command-line tools (no root?)</string>
|
||||
@@ -117,29 +147,29 @@
|
||||
<string name="tools_installer_working">Installing wg and wg-quick</string>
|
||||
<string name="tools_unavailable_error">Required tools unavailable</string>
|
||||
<string name="tun_create_error">Unable to create tun device</string>
|
||||
<string name="tunnel_config_error">Unable to configure tunnel (wg-quick returned %i)</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>
|
||||
<string name="tunnel_create_success">Successfully created tunnel “%s”</string>
|
||||
<string name="tunnel_error_already_exists">Tunnel %s already exists</string>
|
||||
<string name="tunnel_error_empty_interface_address">Address is empty</string>
|
||||
<string name="tunnel_error_empty_peer_public_key">Peer public key may not be empty</string>
|
||||
<string name="tunnel_error_forbidden_endpoint_chars">Forbidden characters in endpoint</string>
|
||||
<string name="tunnel_error_already_exists">Tunnel “%s” already exists</string>
|
||||
<string name="tunnel_error_invalid_name">Invalid name</string>
|
||||
<string name="tunnel_list_placeholder">Add a tunnel using the blue button</string>
|
||||
<string name="tunnel_name">Tunnel Name</string>
|
||||
<string name="tunnel_on_error">Unable to turn tunnel on (wgTurnOn returned %i)</string>
|
||||
<string name="tunnel_on_error">Unable to turn tunnel on (wgTurnOn returned %d)</string>
|
||||
<string name="tunnel_rename_error">Unable to rename tunnel: %s</string>
|
||||
<string name="tunnel_rename_success">Successfully renamed tunnel to “%s”</string>
|
||||
<string name="type_name_go_userspace">Go userspace</string>
|
||||
<string name="type_name_kernel_module">Kernel module</string>
|
||||
<string name="unknown_error">Unknown error</string>
|
||||
<string name="version_summary">%1$s backend v%2$s</string>
|
||||
<string name="version_summary_checking">Checking %s backend version</string>
|
||||
<string name="version_summary_unknown">Unknown %s version</string>
|
||||
<string name="version_title">WireGuard for Android v%s"</string>
|
||||
<string name="vpn_not_authed_error">VPN service not authorized by user</string>
|
||||
<string name="version_title">WireGuard for Android v%s</string>
|
||||
<string name="vpn_not_authorized_error">VPN service not authorized by user</string>
|
||||
<string name="vpn_start_error">Unable to start Android VPN service</string>
|
||||
<string name="zip_export_error">Unable to export tunnels: %s</string>
|
||||
<string name="zip_export_success">Saved to %s</string>
|
||||
<string name="zip_export_success">Saved to “%s”</string>
|
||||
<string name="zip_export_summary">Zip file will be saved to downloads folder</string>
|
||||
<string name="zip_exporter_title">Export tunnels to zip file</string>
|
||||
<string name="zip_export_title">Export tunnels to zip file</string>
|
||||
<string name="key_length_error">Incorrect key length</string>
|
||||
<string name="key_contents_error">Bad characters in key</string>
|
||||
</resources>
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
<item name="colorAccent">@color/accent</item>
|
||||
</style>
|
||||
|
||||
<style name="SettingsTheme" parent="AppTheme">
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
|
||||
</style>
|
||||
|
||||
<style name="fab_label" parent="TextAppearance.AppCompat.Inverse">
|
||||
<item name="android:background">@drawable/fab_label_background</item>
|
||||
<item name="android:textColor">@color/fab_label_text_color</item>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<com.wireguard.android.preference.VersionPreference android:icon="@mipmap/ic_launcher" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="restore_on_boot"
|
||||
android:summary="@string/restore_on_boot_summary"
|
||||
android:title="@string/restore_on_boot_title" />
|
||||
<com.wireguard.android.preference.ModuleDownloaderPreference android:key="module_downloader" />
|
||||
<com.wireguard.android.preference.ToolsInstallerPreference android:key="tools_installer" />
|
||||
<com.wireguard.android.preference.ZipExporterPreference />
|
||||
<com.wireguard.android.preference.LogExporterPreference />
|
||||
@@ -15,4 +16,5 @@
|
||||
android:summaryOff="@string/dark_theme_summary_off"
|
||||
android:summaryOn="@string/dark_theme_summary_on"
|
||||
android:title="@string/dark_theme_title" />
|
||||
</PreferenceScreen>
|
||||
<com.wireguard.android.preference.DonatePreference />
|
||||
</androidx.preference.PreferenceScreen>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Copyright © 2018 WireGuard LLC. All Rights Reserved.
|
||||
# Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
|
||||
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
|
||||
@@ -9,7 +9,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold")
|
||||
|
||||
add_executable(libwg-quick.so wireguard/src/tools/wg-quick/android.c ndk-compat/compat.c)
|
||||
target_compile_options(libwg-quick.so PUBLIC -O3 -std=gnu11 -Wall -pedantic -Wno-missing-field-initializers -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DWG_PACKAGE_NAME=\"${ANDROID_PACKAGE_NAME}\")
|
||||
target_compile_options(libwg-quick.so PUBLIC -O3 -std=gnu11 -Wall -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DWG_PACKAGE_NAME=\"${ANDROID_PACKAGE_NAME}\")
|
||||
target_link_libraries(libwg-quick.so -ldl)
|
||||
|
||||
file(GLOB WG_SOURCES wireguard/src/tools/*.c libmnl/src/*.c ndk-compat/compat.c)
|
||||
add_executable(libwg.so ${WG_SOURCES})
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
build/
|
||||
.gobuildversion
|
||||
build/
|
||||
+10
-40
@@ -1,9 +1,6 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
||||
|
||||
UPSTREAM_FILES := $(filter-out %/main.go %/queueconstants.go,$(wildcard ../wireguard-go/*/*.go) $(wildcard ../wireguard-go/*.go)) ../wireguard-go/go.mod ../wireguard-go/go.sum
|
||||
DOWNSTREAM_FILES := $(wildcard src/*.go) $(wildcard src/*.c) $(wildcard src/*/*.go)
|
||||
# Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
|
||||
BUILDDIR ?= $(CURDIR)/build
|
||||
DESTDIR ?= $(CURDIR)/out
|
||||
@@ -23,43 +20,16 @@ export GOARCH := $(NDK_GO_ARCH_MAP_$(ANDROID_ARCH_NAME))
|
||||
export GOOS := android
|
||||
export CGO_ENABLED := 1
|
||||
|
||||
DESIRED_GO_VERSION := 1.13.1
|
||||
|
||||
default: $(DESTDIR)/libwg-go.so
|
||||
|
||||
GOBUILDARCH := $(NDK_GO_ARCH_MAP_$(shell uname -m))
|
||||
GOBUILDOS := $(shell uname -s | tr '[:upper:]' '[:lower:]')
|
||||
GOBUILDVERSION := 1.11.2
|
||||
GOBUILDTARBALL := https://dl.google.com/go/go$(GOBUILDVERSION).$(GOBUILDOS)-$(GOBUILDARCH).tar.gz
|
||||
GOBUILDVERSION_NEEDED := go version go$(GOBUILDVERSION) $(GOBUILDOS)/$(GOBUILDARCH)
|
||||
export GOROOT := $(BUILDDIR)/goroot
|
||||
export GOPATH := $(BUILDDIR)/gopath
|
||||
export PATH := $(GOROOT)/bin:$(PATH)
|
||||
GOBUILDVERSION_CURRENT := $(shell $(GOROOT)/bin/go version 2>/dev/null)
|
||||
ifneq ($(GOBUILDVERSION_NEEDED),$(GOBUILDVERSION_CURRENT))
|
||||
$(shell rm -f $(GOROOT)/bin/go)
|
||||
endif
|
||||
$(GOROOT)/bin/go:
|
||||
rm -rf "$(GOROOT)"
|
||||
mkdir -p "$(GOROOT)"
|
||||
curl "$(GOBUILDTARBALL)" | tar -C "$(GOROOT)" --strip-components=1 -xzf - || { rm -rf "$(GOROOT)"; exit 1; }
|
||||
cat goruntime-*.diff | patch -p1 -f -N -r- -d "$(GOROOT)" || { rm -rf "$(GOROOT)"; exit 1; }
|
||||
|
||||
$(shell test "$$(cat .gobuildversion 2>/dev/null)" = "$(GOBUILDVERSION_CURRENT)" || rm -f "$(DESTDIR)/libwg-go.so")
|
||||
|
||||
define copy-src-to-build
|
||||
$(subst $(1),$(BUILDDIR)/,$(2)): $(2)
|
||||
@mkdir -vp "$$(dir $$@)"
|
||||
@cp -vp "$$<" "$$@"
|
||||
$(BUILDDIR)/.prepared: $(subst $(1),$(BUILDDIR)/,$(2))
|
||||
endef
|
||||
|
||||
$(foreach FILE,$(UPSTREAM_FILES),$(eval $(call copy-src-to-build,../wireguard-go/,$(FILE))))
|
||||
$(foreach FILE,$(DOWNSTREAM_FILES),$(eval $(call copy-src-to-build,src/,$(FILE))))
|
||||
|
||||
$(BUILDDIR)/.prepared: $(GOROOT)/bin/go
|
||||
cd "$(BUILDDIR)" && go get || { chmod -fR +w "$(GOPATH)/pkg/mod"; rm -rf "$(GOPATH)/pkg/mod"; exit 1; }
|
||||
chmod -fR +w "$(GOPATH)/pkg/mod"
|
||||
$(BUILDDIR)/go-$(DESIRED_GO_VERSION)/.prepared:
|
||||
mkdir -p "$(dir $@)"
|
||||
curl "https://dl.google.com/go/go$(DESIRED_GO_VERSION).$(shell uname -s | tr '[:upper:]' '[:lower:]')-$(NDK_GO_ARCH_MAP_$(shell uname -m)).tar.gz" | tar -C "$(dir $@)" --strip-components=1 -xzf -
|
||||
patch -p1 -f -N -r- -d "$(dir $@)" < goruntime-boottime-over-monotonic.diff
|
||||
touch "$@"
|
||||
|
||||
$(DESTDIR)/libwg-go.so: $(BUILDDIR)/.prepared
|
||||
cd "$(BUILDDIR)" && go build -ldflags="-X main.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard" -v -o "$@" -buildmode c-shared
|
||||
go version > .gobuildversion
|
||||
$(DESTDIR)/libwg-go.so: export PATH := $(BUILDDIR)/go-$(DESIRED_GO_VERSION)/bin/:$(PATH)
|
||||
$(DESTDIR)/libwg-go.so: $(BUILDDIR)/go-$(DESIRED_GO_VERSION)/.prepared go.mod
|
||||
go build -tags linux -ldflags="-X golang.zx2c4.com/wireguard/ipc.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard" -v -trimpath -o "$@" -buildmode c-shared
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
@@ -11,9 +11,10 @@ import "C"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"git.zx2c4.com/wireguard-go/tun"
|
||||
"golang.org/x/sys/unix"
|
||||
"io/ioutil"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"log"
|
||||
"math"
|
||||
"net"
|
||||
@@ -35,14 +36,14 @@ func (l AndroidLogger) Write(p []byte) (int, error) {
|
||||
}
|
||||
|
||||
type TunnelHandle struct {
|
||||
device *Device
|
||||
device *device.Device
|
||||
uapi net.Listener
|
||||
}
|
||||
|
||||
var tunnelHandles map[int32]TunnelHandle
|
||||
|
||||
func init() {
|
||||
roamingDisabled = true
|
||||
device.RoamingDisabled = true
|
||||
tunnelHandles = make(map[int32]TunnelHandle)
|
||||
signals := make(chan os.Signal)
|
||||
signal.Notify(signals, unix.SIGUSR2)
|
||||
@@ -53,7 +54,7 @@ func init() {
|
||||
case <-signals:
|
||||
n := runtime.Stack(buf, true)
|
||||
buf[n] = 0
|
||||
C.__android_log_write(C.ANDROID_LOG_ERROR, C.CString("WireGuard/GoBackend/Stacktrace"), (*_Ctype_char)(unsafe.Pointer(&buf[0])))
|
||||
C.__android_log_write(C.ANDROID_LOG_ERROR, C.CString("WireGuard/GoBackend/Stacktrace"), (*C.char)(unsafe.Pointer(&buf[0])))
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -63,7 +64,7 @@ func init() {
|
||||
func wgTurnOn(ifnameRef string, tunFd int32, settings string) int32 {
|
||||
interfaceName := string([]byte(ifnameRef))
|
||||
|
||||
logger := &Logger{
|
||||
logger := &device.Logger{
|
||||
Debug: log.New(&AndroidLogger{level: C.ANDROID_LOG_DEBUG, interfaceName: interfaceName}, "", 0),
|
||||
Info: log.New(&AndroidLogger{level: C.ANDROID_LOG_INFO, interfaceName: interfaceName}, "", 0),
|
||||
Error: log.New(&AndroidLogger{level: C.ANDROID_LOG_ERROR, interfaceName: interfaceName}, "", 0),
|
||||
@@ -71,7 +72,7 @@ func wgTurnOn(ifnameRef string, tunFd int32, settings string) int32 {
|
||||
|
||||
logger.Debug.Println("Debug log enabled")
|
||||
|
||||
tun, name, err := tun.CreateTUNFromFD(int(tunFd))
|
||||
tun, name, err := tun.CreateUnmonitoredTUNFromFD(int(tunFd))
|
||||
if err != nil {
|
||||
unix.Close(int(tunFd))
|
||||
logger.Error.Println(err)
|
||||
@@ -79,25 +80,22 @@ func wgTurnOn(ifnameRef string, tunFd int32, settings string) int32 {
|
||||
}
|
||||
|
||||
logger.Info.Println("Attaching to interface", name)
|
||||
device := NewDevice(tun, logger)
|
||||
device := device.NewDevice(tun, logger)
|
||||
|
||||
logger.Debug.Println("Interface has MTU", device.tun.mtu)
|
||||
|
||||
bufferedSettings := bufio.NewReadWriter(bufio.NewReader(strings.NewReader(settings)), bufio.NewWriter(ioutil.Discard))
|
||||
setError := ipcSetOperation(device, bufferedSettings)
|
||||
setError := device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings)))
|
||||
if setError != nil {
|
||||
unix.Close(int(tunFd))
|
||||
logger.Error.Println(setError)
|
||||
return -1
|
||||
}
|
||||
|
||||
var uapi net.Listener;
|
||||
var uapi net.Listener
|
||||
|
||||
uapiFile, err := UAPIOpen(name)
|
||||
uapiFile, err := ipc.UAPIOpen(name)
|
||||
if err != nil {
|
||||
logger.Error.Println(err)
|
||||
} else {
|
||||
uapi, err = UAPIListen(name, uapiFile)
|
||||
uapi, err = ipc.UAPIListen(name, uapiFile)
|
||||
if err != nil {
|
||||
uapiFile.Close()
|
||||
logger.Error.Println(err)
|
||||
@@ -108,7 +106,7 @@ func wgTurnOn(ifnameRef string, tunFd int32, settings string) int32 {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go ipcHandle(device, conn)
|
||||
go device.IpcHandle(conn)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -150,22 +148,11 @@ func wgGetSocketV4(tunnelHandle int32) int32 {
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
native, ok := handle.device.net.bind.(*NativeBind)
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
fd := int32(-1)
|
||||
conn, err := native.ipv4.SyscallConn()
|
||||
fd, err := handle.device.PeekLookAtSocketFd4()
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
err = conn.Control(func(f uintptr) {
|
||||
fd = int32(f)
|
||||
})
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return fd
|
||||
return int32(fd)
|
||||
}
|
||||
|
||||
//export wgGetSocketV6
|
||||
@@ -174,27 +161,16 @@ func wgGetSocketV6(tunnelHandle int32) int32 {
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
native, ok := handle.device.net.bind.(*NativeBind)
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
fd := int32(-1)
|
||||
conn, err := native.ipv6.SyscallConn()
|
||||
fd, err := handle.device.PeekLookAtSocketFd6()
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
err = conn.Control(func(f uintptr) {
|
||||
fd = int32(f)
|
||||
})
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return fd
|
||||
return int32(fd)
|
||||
}
|
||||
|
||||
//export wgVersion
|
||||
func wgVersion() *C.char {
|
||||
return C.CString(WireGuardGoVersion)
|
||||
return C.CString(device.WireGuardGoVersion)
|
||||
}
|
||||
|
||||
func main() {}
|
||||
@@ -0,0 +1,10 @@
|
||||
module golang.zx2c4.com/wireguard/android
|
||||
|
||||
go 1.12
|
||||
|
||||
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
|
||||
)
|
||||
@@ -0,0 +1,19 @@
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||
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/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/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=
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user