Compare commits

..

64 Commits

Author SHA1 Message Date
Jason A. Donenfeld b83538d08d Version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-14 00:24:47 +02:00
Jason A. Donenfeld 3c31c340d8 Download modules after verifying signify signature
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-14 00:03:39 +02:00
Jason A. Donenfeld 59620456ee Revert "Fix activity leak on Android Q"
This reverts commit 489518000971914b2608da43e2146690dcc02cb9.

October has arrived.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-13 14:10:11 +02:00
Jason A. Donenfeld e42bd29382 Version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-13 14:10:11 +02:00
Jason A. Donenfeld 18dbc21f96 libwg-go: overwrite socket directory correctly
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-13 14:09:40 +02:00
Jason A. Donenfeld 68d871c47c Version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-13 11:53:17 +02:00
Jason A. Donenfeld a45a219e5f proguard: reenable obfuscation
Android bundles let us keep everything together.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-13 11:53:17 +02:00
Jason A. Donenfeld 49788240aa libwg-go: version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-13 11:46:41 +02:00
Jason A. Donenfeld 52166500fd ToolsInstaller: extract from apk instead of relying on native extraction
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-13 11:46:41 +02:00
Jason A. Donenfeld 3c8fef2655 SharedLibraryLoader: separate out extraction
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-13 11:46:41 +02:00
Jason A. Donenfeld 21af2f2f62 libwg-go: overwrite socket directory correctly
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-13 11:46:41 +02:00
Jason A. Donenfeld 6d01296e8b SharedLibraryLoader: prioritize ABI ordering
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-13 11:46:41 +02:00
Jason A. Donenfeld 749efcde21 SharedLibraryLoader: iterate through all apks for bundles
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-13 11:46:41 +02:00
Jason A. Donenfeld 3af2420da9 Version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-12 18:55:28 +02:00
Nicolas Douma d0d24f4554 tools: prepare for binder usage in wg-quick
Signed-off-by: Nicolas Douma <nicolas@serveur.io>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-12 18:55:28 +02:00
Jason A. Donenfeld 96b44c1771 Activity: make dark/night theme follow system on Q
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-12 18:55:28 +02:00
Jason A. Donenfeld 4a1d07b364 Application: use preferences from compat libs
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-12 18:55:28 +02:00
Jason A. Donenfeld 7fbe5349a2 export: use content resolver on android Q+
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-12 18:55:28 +02:00
Jason A. Donenfeld d8bad72fd6 preferences: add donation link
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-12 18:55:28 +02:00
Jason A. Donenfeld 927b32c99f libwg-go: update to go 1.13
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-12 18:55:21 +02:00
Jason A. Donenfeld 27b691bef6 idea: update settings for 3.5
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-10-12 16:51:15 +02:00
Harsh Shandilya d2b9de740d Migrate to Android 10
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2019-09-30 19:45:11 +02:00
Jason A. Donenfeld eb45b4b1bd InetAddresses: prepare for Android 10's real method support
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-09-30 19:42:02 +02:00
Harsh Shandilya c545b5e65f FragmentUtils: Directly cast context as SettingsActivity
ContextThemeWrapper#getContext seems to be an instance of ContextImpl now which
is not public API and also not what we want. Directly cast context as SettingsActivity
which seems to work exactly how we need this to.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2019-09-27 17:26:56 +05:30
Harsh Shandilya 992b6486a1 Update runtime dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2019-09-27 13:27:51 +05:30
Harsh Shandilya 421b1f889b Add LeakCanary to debug builds
LeakCanary is an advanced memory leak detection library for Android designed by the fine folks
at Square.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2019-09-27 13:18:27 +05:30
Harsh Shandilya 1ee1368e18 Fix activity leak on Android Q
This workaround was discussed at https://twitter.com/Piwai/status/1169274622614704129 after
Google had closed the issuetracker with a WONTFIX at https://issuetracker.google.com/issues/139738913.

The situation has since changed with Google promising a fix on October's ASB but since we can't really
know, patch this ourselves for the timebeing.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2019-09-27 13:18:27 +05:30
Harsh Shandilya 1a6a8789c1 Update to Golang 1.12.10
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2019-09-27 13:15:06 +05:30
Harsh Shandilya b5c155db1b Update AGP to 3.5.0
Also update the gitignore to ignore the '.cxx' directory AGP 3.5.0
uses for native build artifacts.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2019-09-27 13:15:06 +05:30
Harsh Shandilya 6c5b46eadd Uprev to Gradle 5.6.2
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2019-09-27 12:59:53 +05:30
Jason A. Donenfeld 5abbab2635 Version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-07-08 15:46:39 +02:00
Jason A. Donenfeld e5766094f4 tools: bump deps
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-07-08 15:46:04 +02:00
Jason A. Donenfeld 96d77988d3 Version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-06-05 21:08:47 +02:00
Revath S Kumar e9e4fd4e8b TunnelList: Get focus to toggle button via remote for Fire TV
This removes a no-longer-needed workaround for the ListView
OnItemClickListener (it won't fire if a focusable view is inside the
item view). Since converting our ListView instances to RecyclerView
instances, we set the OnClick and OnLongClick listeners directly on the
item view, and this workaround no longer has any effect.

Unsurprisingly, the workaround breaks focusability of the Switch, which
is necessary to toggle tunnels on devices with keypad-based navigation,
such as the Fire TV.

This commit also adds explicit focusability hints for the Switch.

Related mail thread:
https://lists.zx2c4.com/pipermail/wireguard/2019-May/004112.html

Reported-by: Christophe-Marie Duquesne <chmd@chmd.fr>
Reported-by: Revath S Kumar <gmail@revathskumar.com>
[Samuel: sorted attributes; expanded commit message]
Signed-off-by: Samuel Holland <samuel@sholland.org>
2019-06-05 21:07:50 +02:00
Jason A. Donenfeld b5d9fbf1f4 libwg-go: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-06-05 21:07:50 +02:00
Jason A. Donenfeld 81dc89f85b Version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-03-19 00:56:13 -06:00
Jason A. Donenfeld 76305045db InetEndpoint: properly match IPv6 addresses
The old one didn't account for trailing digits.

Reported-by: Brandon Jackson <bjackson@napshome.net>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-03-19 00:53:14 -06:00
Jason A. Donenfeld c4ba48d7a9 libwg-go: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-03-19 00:40:45 -06:00
Jason A. Donenfeld d5dcdf13bf strings: %i is invalid
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-03-19 00:30:53 -06:00
Jason A. Donenfeld 7feb3dccbf WgQuickBackend: pass result to error string
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-03-16 12:24:28 -06:00
Jason A. Donenfeld 2251d74fce Version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-03-08 01:41:41 +01:00
Jason A. Donenfeld 2c89d3fa7f libwg-go: use netpoll rather than rwcancel
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-03-07 02:05:42 +01:00
Jason A. Donenfeld 49cc634678 libwg-go: don't use submodule
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-03-03 05:43:42 +01:00
Jason A. Donenfeld c93e81c632 libwg-go: update to 1.12
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-02-27 05:24:25 +01:00
Jason A. Donenfeld 050e202291 project: bump dependencies
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-02-17 04:14:11 +01:00
Jason A. Donenfeld efe04d6602 ToolsInstaller: fix typo on cleanup
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-02-17 03:32:35 +01:00
Jason A. Donenfeld 823b8324c7 Version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-02-15 18:03:43 +01:00
Jason A. Donenfeld ae2f88a9ee tools: bump upstream version
This now includes the latest ARM64 ChaCha20 implementation, which should
improve performance.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-02-14 11:59:10 +01:00
Jason A. Donenfeld 32287c60c4 ToolsInstaller: Require Magisk 18
People installing magisk modules are people capable of updating Magisk.
No need to leave around old compat cruft.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-02-14 11:59:10 +01:00
Harsh Shandilya 69c6fa0a24 ToolsInstaller: Use chcon over restorecon
restorecon probes file_contexts to get the context
to be applied to the file. /sbin/.magisk does not
exist in file_contexts for obvious reasons so restorecon
always fails. Use chcon directly with the system_file
context to allow contexts to be applied.

Suggested-by: Chris Renshaw <osm0sis@outlook.com>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2019-02-14 11:03:41 +01:00
Jason A. Donenfeld c3e63df7b5 Update copyright
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-01-07 19:21:34 -05:00
Samuel Holland fb4f1e30d5 PeerProxy: Only add IPv4 DNS servers when excluding private IPs
Reported-By: Christophe-Marie Duquesne <chmd@chmd.fr>

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-01-07 19:21:34 -05:00
Harsh Shandilya 6d0fde218b Bump stream support libraries to latest stable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2018-12-18 17:58:55 +01:00
Jason A. Donenfeld 6d02b0a26d strings: squelch warning on multiple %s
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-12-18 17:58:12 +01:00
Harsh Shandilya 495bf9c954 Enable proguard and wire up rules
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2018-12-18 22:18:00 +05:30
Jason A. Donenfeld cb1bc95e6b Version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-12-18 17:07:04 +01:00
Harsh Shandilya 6fdf0266cf Migrate to AndroidX
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2018-12-18 16:54:54 +01:00
Jason A. Donenfeld ee26198e2c Bump the go runtime
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-12-16 01:54:22 +01:00
Jason A. Donenfeld fe424197da InetEndpoint: disallow huge ports
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-12-15 22:05:43 +01:00
Samuel Holland 053ca232aa Adjust code generation to match existing style
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-12-15 14:46:23 -06:00
Samuel Holland 2e8d566bd4 Clean up error messages
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-12-15 14:46:23 -06:00
Samuel Holland dcb0e9b3e8 Provide semantically meaningful exceptions for translation
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-12-15 14:46:23 -06:00
Jason A. Donenfeld 3497882ea6 Bump the go runtime
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-12-15 06:13:55 +01:00
Jason A. Donenfeld dc2463a0ab Fix locale usage
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-12-11 03:47:32 +01:00
114 changed files with 1606 additions and 1221 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
/captures/
/local.properties
.DS_Store
.externalNativeBuild/
.cxx/
Thumbs.db
build/
*.apk
-3
View File
@@ -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
+3 -38
View File
@@ -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>
+1 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
*/
+3
View File
@@ -0,0 +1,3 @@
# Squelch all warnings, they're harmless but ProGuard
# escalates them as errors.
-dontwarn sun.misc.Unsafe
+1 -3
View File
@@ -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;
@@ -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;
@@ -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);
@@ -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;
+39 -25
View File
@@ -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&lt;com.wireguard.android.viewmodel.PeerProxy&gt;" />
type="androidx.databinding.ObservableList&lt;com.wireguard.android.viewmodel.PeerProxy&gt;" />
<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&lt;String, Tunnel&gt;" />
</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>
+5 -1
View File
@@ -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>
+64 -34
View File
@@ -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 &lt; tunnel.conf`.</string>
<string name="qr_code_hint">Tip: generate with `qrencode -t ansiutf8 &lt; 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>
-4
View File
@@ -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>
+4 -2
View File
@@ -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>
+3 -2
View File
@@ -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
View File
@@ -1,2 +1 @@
build/
.gobuildversion
build/
+10 -40
View File
@@ -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() {}
+10
View File
@@ -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
)
+19
View File
@@ -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