AmneziaWG release (#4)

This commit is contained in:
albexk
2024-03-16 18:34:34 +03:00
committed by GitHub
parent d6f973d620
commit 1f748dc917
180 changed files with 2691 additions and 5194 deletions

18
.gitignore vendored Normal file
View File

@@ -0,0 +1,18 @@
/.gradle/
/.idea/*.xml
/.idea/caches/
/.idea/dictionaries/
/.idea/libraries/
/captures/
/local.properties
.DS_Store
.cxx/
Thumbs.db
build/
*.apk
*.class
*.dex
*.iml
*.jks
gradlew.bat
maint/

View File

@@ -1,40 +1,15 @@
# Android GUI for [WireGuard](https://www.wireguard.com/)
# Android GUI for [AmneziaWG](https://amnezia.org/learn-more/31_amneziawg)
**[Download from the Play Store](https://play.google.com/store/apps/details?id=com.wireguard.android)**
**[Download from the Play Store](https://play.google.com/store/apps/details?id=org.amnezia.awg)**
This is an Android GUI for [WireGuard](https://www.wireguard.com/). It [opportunistically uses the kernel implementation](https://git.zx2c4.com/android_kernel_wireguard/about/), and falls back to using the non-root [userspace implementation](https://git.zx2c4.com/wireguard-go/about/).
This is an Android GUI for [AmneziaWG](https://amnezia.org/learn-more/31_amneziawg).
## Building
```
$ git clone --recurse-submodules https://git.zx2c4.com/wireguard-android
$ cd wireguard-android
$ git clone --recurse-submodules https://github.com/amnezia-vpn/amneziawg-android
$ cd amneziawg-android
$ ./gradlew assembleRelease
```
macOS users may need [flock(1)](https://github.com/discoteq/flock).
## Embedding
The tunnel library is [on Maven Central](https://search.maven.org/artifact/com.wireguard.android/tunnel), alongside [extensive class library documentation](https://javadoc.io/doc/com.wireguard.android/tunnel).
```
implementation 'com.wireguard.android:tunnel:$wireguardTunnelVersion'
```
The library makes use of Java 8 features, so be sure to support those in your gradle configuration with [desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring):
```
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
coreLibraryDesugaringEnabled = true
}
dependencies {
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.3"
}
```
## Translating
Please help us translate the app into several languages on [our translation platform](https://crowdin.com/project/WireGuard).

View File

@@ -1,6 +1,6 @@
amneziawgVersionCode=1
amneziawgVersionName=1.0.0
amneziawgPackageName=org.amnezia.vpn
amneziawgVersionCode=3
amneziawgVersionName=1.1.0
amneziawgPackageName=org.amnezia.awg
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit

View File

@@ -1,25 +1,25 @@
[versions]
agp = "8.2.0"
agp = "8.3.0"
kotlin = "1.9.20"
[libraries]
androidx-activity-ktx = "androidx.activity:activity-ktx:1.7.2"
androidx-annotation = "androidx.annotation:annotation:1.6.0"
androidx-activity-ktx = "androidx.activity:activity-ktx:1.8.2"
androidx-annotation = "androidx.annotation:annotation:1.7.1"
androidx-appcompat = "androidx.appcompat:appcompat:1.6.1"
androidx-biometric = "androidx.biometric:biometric:1.1.0"
androidx-collection = "androidx.collection:collection:1.2.0"
androidx-collection = "androidx.collection:collection:1.4.0"
androidx-constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4"
androidx-coordinatorlayout = "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
androidx-core-ktx = "androidx.core:core-ktx:1.10.1"
androidx-core-ktx = "androidx.core:core-ktx:1.12.0"
androidx-datastore-preferences = "androidx.datastore:datastore-preferences:1.0.0"
androidx-fragment-ktx = "androidx.fragment:fragment-ktx:1.5.7"
androidx-lifecycle-runtime-ktx = "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1"
androidx-preference-ktx = "androidx.preference:preference-ktx:1.2.0"
desugarJdkLibs = "com.android.tools:desugar_jdk_libs:2.0.3"
google-material = "com.google.android.material:material:1.9.0"
androidx-fragment-ktx = "androidx.fragment:fragment-ktx:1.6.2"
androidx-lifecycle-runtime-ktx = "androidx.lifecycle:lifecycle-runtime-ktx:2.7.0"
androidx-preference-ktx = "androidx.preference:preference-ktx:1.2.1"
desugarJdkLibs = "com.android.tools:desugar_jdk_libs:2.0.4"
google-material = "com.google.android.material:material:1.11.0"
jsr305 = "com.google.code.findbugs:jsr305:3.0.2"
junit = "junit:junit:4.13.2"
kotlinx-coroutines-android = "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0"
kotlinx-coroutines-android = "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
zxing-android-embedded = "com.journeyapps:zxing-android-embedded:4.3.0"
[plugins]

View File

@@ -18,7 +18,7 @@ dependencyResolutionManagement {
}
plugins {
id("com.android.settings") version "8.2.0"
id("com.android.settings") version "8.3.0"
}
rootProject.name = "amneziawg-android"

View File

@@ -1,4 +0,0 @@
#!/bin/bash
set -ex
curl -Lo - https://crowdin.com/backend/download/project/wireguard.zip | bsdtar -C ui/src/main/res -x -f - --strip-components 5 wireguard-android
find ui/src/main/res -name strings.xml -exec bash -c '[[ $(xmllint --xpath "count(//resources/*)" {}) -ne 0 ]] || rm -rf "$(dirname {})"' \;

View File

@@ -3,11 +3,10 @@
import org.gradle.api.tasks.testing.logging.TestLogEvent
val pkg: String = providers.gradleProperty("amneziawgPackageName").get()
val cmakeAndroidPackageName: String = providers.environmentVariable("ANDROID_PACKAGE_NAME").getOrElse(pkg)
plugins {
alias(libs.plugins.android.library)
`maven-publish`
signing
}
android {
@@ -36,14 +35,14 @@ android {
release {
externalNativeBuild {
cmake {
arguments("-DANDROID_PACKAGE_NAME=${pkg}")
arguments("-DANDROID_PACKAGE_NAME=${cmakeAndroidPackageName}")
}
}
}
debug {
externalNativeBuild {
cmake {
arguments("-DANDROID_PACKAGE_NAME=${pkg}.debug")
arguments("-DANDROID_PACKAGE_NAME=${cmakeAndroidPackageName}.debug")
}
}
}
@@ -52,12 +51,6 @@ android {
disable += "LongLogTag"
disable += "NewApi"
}
publishing {
singleVariant("release") {
withJavadocJar()
withSourcesJar()
}
}
}
dependencies {
@@ -66,59 +59,3 @@ dependencies {
compileOnly(libs.jsr305)
testImplementation(libs.junit)
}
publishing {
publications {
register<MavenPublication>("release") {
groupId = pkg
artifactId = "tunnel"
version = providers.gradleProperty("amneziawgVersionName").get()
afterEvaluate {
from(components["release"])
}
pom {
name.set("WireGuard Tunnel Library")
description.set("Embeddable tunnel library for WireGuard for Android")
url.set("https://www.wireguard.com/")
licenses {
license {
name.set("The Apache Software License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
distribution.set("repo")
}
}
scm {
connection.set("scm:git:https://git.zx2c4.com/wireguard-android")
developerConnection.set("scm:git:https://git.zx2c4.com/wireguard-android")
url.set("https://git.zx2c4.com/wireguard-android")
}
developers {
organization {
name.set("WireGuard")
url.set("https://www.wireguard.com/")
}
developer {
name.set("WireGuard")
email.set("team@wireguard.com")
}
}
}
}
}
repositories {
maven {
name = "sonatype"
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = providers.environmentVariable("SONATYPE_USER").orNull
password = providers.environmentVariable("SONATYPE_PASSWORD").orNull
}
}
}
}
signing {
useGpgCmd()
sign(publishing.publications)
}

View File

@@ -7,7 +7,7 @@
<application>
<service
android:name="com.wireguard.android.backend.GoBackend$VpnService"
android:name="org.amnezia.awg.backend.GoBackend$VpnService"
android:permission="android.permission.BIND_VPN_SERVICE"
android:exported="false">
<intent-filter>

View File

@@ -1,423 +0,0 @@
/*
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.config;
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 com.wireguard.util.NonNullForAll;
import java.net.InetAddress;
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.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import androidx.annotation.Nullable;
/**
* Represents the configuration for a WireGuard interface (an [Interface] block). Interfaces must
* have a private key (used to initialize a {@code KeyPair}), and may optionally have several other
* attributes.
* <p>
* Instances of this class are immutable.
*/
@NonNullForAll
public final class Interface {
private static final int MAX_UDP_PORT = 65535;
private static final int MIN_UDP_PORT = 0;
private final Set<InetNetwork> addresses;
private final Set<InetAddress> dnsServers;
private final Set<String> dnsSearchDomains;
private final Set<String> excludedApplications;
private final Set<String> includedApplications;
private final KeyPair keyPair;
private final Optional<Integer> listenPort;
private final Optional<Integer> mtu;
private Interface(final Builder builder) {
// 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));
dnsSearchDomains = Collections.unmodifiableSet(new LinkedHashSet<>(builder.dnsSearchDomains));
excludedApplications = Collections.unmodifiableSet(new LinkedHashSet<>(builder.excludedApplications));
includedApplications = Collections.unmodifiableSet(new LinkedHashSet<>(builder.includedApplications));
keyPair = Objects.requireNonNull(builder.keyPair, "Interfaces must have a private key");
listenPort = builder.listenPort;
mtu = builder.mtu;
}
/**
* Parses an series of "KEY = VALUE" lines into an {@code Interface}. Throws
* {@link ParseException} if the input is not well-formed or contains unknown attributes.
*
* @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 BadConfigException {
final Builder builder = new Builder();
for (final CharSequence line : lines) {
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());
break;
case "dns":
builder.parseDnsServers(attribute.getValue());
break;
case "excludedapplications":
builder.parseExcludedApplications(attribute.getValue());
break;
case "includedapplications":
builder.parseIncludedApplications(attribute.getValue());
break;
case "listenport":
builder.parseListenPort(attribute.getValue());
break;
case "mtu":
builder.parseMtu(attribute.getValue());
break;
case "privatekey":
builder.parsePrivateKey(attribute.getValue());
break;
default:
throw new BadConfigException(Section.INTERFACE, Location.TOP_LEVEL,
Reason.UNKNOWN_ATTRIBUTE, attribute.getKey());
}
}
return builder.build();
}
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof Interface))
return false;
final Interface other = (Interface) obj;
return addresses.equals(other.addresses)
&& dnsServers.equals(other.dnsServers)
&& dnsSearchDomains.equals(other.dnsSearchDomains)
&& excludedApplications.equals(other.excludedApplications)
&& includedApplications.equals(other.includedApplications)
&& keyPair.equals(other.keyPair)
&& listenPort.equals(other.listenPort)
&& mtu.equals(other.mtu);
}
/**
* Returns the set of IP addresses assigned to the interface.
*
* @return a set of {@link InetNetwork}s
*/
public Set<InetNetwork> getAddresses() {
// The collection is already immutable.
return addresses;
}
/**
* Returns the set of DNS servers associated with the interface.
*
* @return a set of {@link InetAddress}es
*/
public Set<InetAddress> getDnsServers() {
// The collection is already immutable.
return dnsServers;
}
/**
* Returns the set of DNS search domains associated with the interface.
*
* @return a set of strings
*/
public Set<String> getDnsSearchDomains() {
// The collection is already immutable.
return dnsSearchDomains;
}
/**
* Returns the set of applications excluded from using the interface.
*
* @return a set of package names
*/
public Set<String> getExcludedApplications() {
// The collection is already immutable.
return excludedApplications;
}
/**
* Returns the set of applications included exclusively for using the interface.
*
* @return a set of package names
*/
public Set<String> getIncludedApplications() {
// The collection is already immutable.
return includedApplications;
}
/**
* Returns the public/private key pair used by the interface.
*
* @return a key pair
*/
public KeyPair getKeyPair() {
return keyPair;
}
/**
* Returns the UDP port number that the WireGuard interface will listen on.
*
* @return a UDP port number, or {@code Optional.empty()} if none is configured
*/
public Optional<Integer> getListenPort() {
return listenPort;
}
/**
* Returns the MTU used for the WireGuard interface.
*
* @return the MTU, or {@code Optional.empty()} if none is configured
*/
public Optional<Integer> getMtu() {
return mtu;
}
@Override
public int hashCode() {
int hash = 1;
hash = 31 * hash + addresses.hashCode();
hash = 31 * hash + dnsServers.hashCode();
hash = 31 * hash + excludedApplications.hashCode();
hash = 31 * hash + includedApplications.hashCode();
hash = 31 * hash + keyPair.hashCode();
hash = 31 * hash + listenPort.hashCode();
hash = 31 * hash + mtu.hashCode();
return hash;
}
/**
* Converts the {@code Interface} into a string suitable for debugging purposes. The {@code
* Interface} is identified by its public key and (if set) the port used for its UDP socket.
*
* @return A concise single-line identifier for the {@code Interface}
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("(Interface ");
sb.append(keyPair.getPublicKey().toBase64());
listenPort.ifPresent(lp -> sb.append(" @").append(lp));
sb.append(')');
return sb.toString();
}
/**
* Converts the {@code Interface} into a string suitable for inclusion in a {@code wg-quick}
* configuration file.
*
* @return The {@code Interface} represented as a series of "Key = Value" lines
*/
public String toWgQuickString() {
final StringBuilder sb = new StringBuilder();
if (!addresses.isEmpty())
sb.append("Address = ").append(Attribute.join(addresses)).append('\n');
if (!dnsServers.isEmpty()) {
final List<String> dnsServerStrings = dnsServers.stream().map(InetAddress::getHostAddress).collect(Collectors.toList());
dnsServerStrings.addAll(dnsSearchDomains);
sb.append("DNS = ").append(Attribute.join(dnsServerStrings)).append('\n');
}
if (!excludedApplications.isEmpty())
sb.append("ExcludedApplications = ").append(Attribute.join(excludedApplications)).append('\n');
if (!includedApplications.isEmpty())
sb.append("IncludedApplications = ").append(Attribute.join(includedApplications)).append('\n');
listenPort.ifPresent(lp -> sb.append("ListenPort = ").append(lp).append('\n'));
mtu.ifPresent(m -> sb.append("MTU = ").append(m).append('\n'));
sb.append("PrivateKey = ").append(keyPair.getPrivateKey().toBase64()).append('\n');
return sb.toString();
}
/**
* Serializes the {@code Interface} for use with the WireGuard cross-platform userspace API.
* Note that not all attributes are included in this representation.
*
* @return the {@code Interface} represented as a series of "KEY=VALUE" lines
*/
public String toWgUserspaceString() {
final StringBuilder sb = new StringBuilder();
sb.append("private_key=").append(keyPair.getPrivateKey().toHex()).append('\n');
listenPort.ifPresent(lp -> sb.append("listen_port=").append(lp).append('\n'));
return sb.toString();
}
@SuppressWarnings("UnusedReturnValue")
public static final class Builder {
// Defaults to an empty set.
private final Set<InetNetwork> addresses = new LinkedHashSet<>();
// Defaults to an empty set.
private final Set<InetAddress> dnsServers = new LinkedHashSet<>();
// Defaults to an empty set.
private final Set<String> dnsSearchDomains = new LinkedHashSet<>();
// Defaults to an empty set.
private final Set<String> excludedApplications = new LinkedHashSet<>();
// Defaults to an empty set.
private final Set<String> includedApplications = new LinkedHashSet<>();
// No default; must be provided before building.
@Nullable private KeyPair keyPair;
// Defaults to not present.
private Optional<Integer> listenPort = Optional.empty();
// Defaults to not present.
private Optional<Integer> mtu = Optional.empty();
public Builder addAddress(final InetNetwork address) {
addresses.add(address);
return this;
}
public Builder addAddresses(final Collection<InetNetwork> addresses) {
this.addresses.addAll(addresses);
return this;
}
public Builder addDnsServer(final InetAddress dnsServer) {
dnsServers.add(dnsServer);
return this;
}
public Builder addDnsServers(final Collection<? extends InetAddress> dnsServers) {
this.dnsServers.addAll(dnsServers);
return this;
}
public Builder addDnsSearchDomain(final String dnsSearchDomain) {
dnsSearchDomains.add(dnsSearchDomain);
return this;
}
public Builder addDnsSearchDomains(final Collection<String> dnsSearchDomains) {
this.dnsSearchDomains.addAll(dnsSearchDomains);
return this;
}
public Interface build() throws BadConfigException {
if (keyPair == null)
throw new BadConfigException(Section.INTERFACE, Location.PRIVATE_KEY,
Reason.MISSING_ATTRIBUTE, null);
if (!includedApplications.isEmpty() && !excludedApplications.isEmpty())
throw new BadConfigException(Section.INTERFACE, Location.INCLUDED_APPLICATIONS,
Reason.INVALID_KEY, null);
return new Interface(this);
}
public Builder excludeApplication(final String application) {
excludedApplications.add(application);
return this;
}
public Builder excludeApplications(final Collection<String> applications) {
excludedApplications.addAll(applications);
return this;
}
public Builder includeApplication(final String application) {
includedApplications.add(application);
return this;
}
public Builder includeApplications(final Collection<String> applications) {
includedApplications.addAll(applications);
return this;
}
public Builder parseAddresses(final CharSequence addresses) throws BadConfigException {
try {
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 BadConfigException {
try {
for (final String dnsServer : Attribute.split(dnsServers)) {
try {
addDnsServer(InetAddresses.parse(dnsServer));
} catch (final ParseException e) {
if (e.getParsingClass() != InetAddress.class || !InetAddresses.isHostname(dnsServer))
throw e;
addDnsSearchDomain(dnsServer);
}
}
return this;
} catch (final ParseException e) {
throw new BadConfigException(Section.INTERFACE, Location.DNS, e);
}
}
public Builder parseExcludedApplications(final CharSequence apps) {
return excludeApplications(List.of(Attribute.split(apps)));
}
public Builder parseIncludedApplications(final CharSequence apps) {
return includeApplications(List.of(Attribute.split(apps)));
}
public Builder parseListenPort(final String listenPort) throws BadConfigException {
try {
return setListenPort(Integer.parseInt(listenPort));
} catch (final NumberFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.LISTEN_PORT, listenPort, e);
}
}
public Builder parseMtu(final String mtu) throws BadConfigException {
try {
return setMtu(Integer.parseInt(mtu));
} catch (final NumberFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.MTU, mtu, e);
}
}
public Builder parsePrivateKey(final String privateKey) throws BadConfigException {
try {
return setKeyPair(new KeyPair(Key.fromBase64(privateKey)));
} catch (final KeyFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.PRIVATE_KEY, e);
}
}
public Builder setKeyPair(final KeyPair keyPair) {
this.keyPair = keyPair;
return this;
}
public Builder setListenPort(final int listenPort) throws BadConfigException {
if (listenPort < MIN_UDP_PORT || listenPort > MAX_UDP_PORT)
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) throws BadConfigException {
if (mtu < 0)
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;
}
}
}

View File

@@ -0,0 +1,18 @@
package org.amnezia.awg;
import androidx.annotation.Nullable;
public class GoBackend {
@Nullable
public static native String awgGetConfig(int handle);
public static native int awgGetSocketV4(int handle);
public static native int awgGetSocketV6(int handle);
public static native void awgTurnOff(int handle);
public static native int awgTurnOn(String ifName, int tunFd, String settings);
public static native String awgVersion();
}

View File

@@ -3,24 +3,23 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.backend;
package org.amnezia.awg.backend;
import android.content.Context;
import android.util.Log;
import android.util.Pair;
import com.wireguard.android.backend.BackendException.Reason;
import com.wireguard.android.backend.Tunnel.State;
import com.wireguard.android.util.RootShell;
import com.wireguard.android.util.ToolsInstaller;
import com.wireguard.config.Config;
import com.wireguard.crypto.Key;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.backend.BackendException.Reason;
import org.amnezia.awg.backend.Tunnel.State;
import org.amnezia.awg.util.RootShell;
import org.amnezia.awg.util.ToolsInstaller;
import org.amnezia.awg.config.Config;
import org.amnezia.awg.crypto.Key;
import org.amnezia.awg.util.NonNullForAll;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -35,27 +34,27 @@ import java.util.Set;
import androidx.annotation.Nullable;
/**
* Implementation of {@link Backend} that uses the kernel module and {@code wg-quick} to provide
* WireGuard tunnels.
* Implementation of {@link Backend} that uses the kernel module and {@code awg-quick} to provide
* AmneziaWG tunnels.
*/
@NonNullForAll
public final class WgQuickBackend implements Backend {
private static final String TAG = "WireGuard/WgQuickBackend";
public final class AwgQuickBackend implements Backend {
private static final String TAG = "AmneziaWG/AwgQuickBackend";
private final File localTemporaryDir;
private final RootShell rootShell;
private final Map<Tunnel, Config> runningConfigs = new HashMap<>();
private final ToolsInstaller toolsInstaller;
private boolean multipleTunnels;
public WgQuickBackend(final Context context, final RootShell rootShell, final ToolsInstaller toolsInstaller) {
public AwgQuickBackend(final Context context, final RootShell rootShell, final ToolsInstaller toolsInstaller) {
localTemporaryDir = new File(context.getCacheDir(), "tmp");
this.rootShell = rootShell;
this.toolsInstaller = toolsInstaller;
}
public static boolean hasKernelSupport() {
return new File("/sys/module/wireguard").exists();
return new File("/sys/module/amneziawg").exists();
}
@Override
@@ -64,13 +63,13 @@ public final class WgQuickBackend implements Backend {
// Don't throw an exception here or nothing will show up in the UI.
try {
toolsInstaller.ensureToolsAvailable();
if (rootShell.run(output, "wg show interfaces") != 0 || output.isEmpty())
if (rootShell.run(output, "awg show interfaces") != 0 || output.isEmpty())
return Collections.emptySet();
} catch (final Exception e) {
Log.w(TAG, "Unable to enumerate running tunnels", e);
return Collections.emptySet();
}
// wg puts all interface names on the same line. Split them into separate elements.
// awg puts all interface names on the same line. Split them into separate elements.
return Set.of(output.get(0).split(" "));
}
@@ -84,7 +83,7 @@ public final class WgQuickBackend implements Backend {
final Statistics stats = new Statistics();
final Collection<String> output = new ArrayList<>();
try {
if (rootShell.run(output, String.format("wg show '%s' dump", tunnel.getName())) != 0)
if (rootShell.run(output, String.format("awg show '%s' dump", tunnel.getName())) != 0)
return stats;
} catch (final Exception ignored) {
return stats;
@@ -104,7 +103,7 @@ public final class WgQuickBackend implements Backend {
@Override
public String getVersion() throws Exception {
final List<String> output = new ArrayList<>();
if (rootShell.run(output, "cat /sys/module/wireguard/version") != 0 || output.isEmpty())
if (rootShell.run(output, "cat /sys/module/amneziawg/version") != 0 || output.isEmpty())
throw new BackendException(Reason.UNKNOWN_KERNEL_MODULE_NAME);
return output.get(0);
}
@@ -174,17 +173,17 @@ public final class WgQuickBackend implements Backend {
final File tempFile = new File(localTemporaryDir, tunnel.getName() + ".conf");
try (final FileOutputStream stream = new FileOutputStream(tempFile, false)) {
stream.write(config.toWgQuickString().getBytes(StandardCharsets.UTF_8));
stream.write(config.toAwgQuickString().getBytes(StandardCharsets.UTF_8));
}
String command = String.format("wg-quick %s '%s'",
String command = String.format("awg-quick %s '%s'",
state.toString().toLowerCase(Locale.ENGLISH), tempFile.getAbsolutePath());
if (state == State.UP)
command = "cat /sys/module/wireguard/version && " + command;
command = "cat /sys/module/amneziawg/version && " + command;
final int result = rootShell.run(null, command);
// noinspection ResultOfMethodCallIgnored
tempFile.delete();
if (result != 0)
throw new BackendException(Reason.WG_QUICK_CONFIG_ERROR_CODE, result);
throw new BackendException(Reason.AWG_QUICK_CONFIG_ERROR_CODE, result);
if (state == State.UP)
runningConfigs.put(tunnel, config);

View File

@@ -3,17 +3,17 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.backend;
package org.amnezia.awg.backend;
import com.wireguard.config.Config;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.config.Config;
import org.amnezia.awg.util.NonNullForAll;
import java.util.Set;
import androidx.annotation.Nullable;
/**
* Interface for implementations of the WireGuard secure network tunnel.
* Interface for implementations of the AmneziaWG secure network tunnel.
*/
@NonNullForAll

View File

@@ -3,9 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.backend;
package org.amnezia.awg.backend;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.util.NonNullForAll;
/**
* A subclass of {@link Exception} that encapsulates the reasons for a failure originating in
@@ -50,7 +50,7 @@ public final class BackendException extends Exception {
*/
public enum Reason {
UNKNOWN_KERNEL_MODULE_NAME,
WG_QUICK_CONFIG_ERROR_CODE,
AWG_QUICK_CONFIG_ERROR_CODE,
TUNNEL_MISSING_CONFIG,
VPN_NOT_AUTHORIZED,
UNABLE_TO_START_VPN,

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.backend;
package org.amnezia.awg.backend;
import android.content.Context;
import android.content.Intent;
@@ -12,19 +12,18 @@ import android.os.ParcelFileDescriptor;
import android.system.OsConstants;
import android.util.Log;
import com.wireguard.android.backend.BackendException.Reason;
import com.wireguard.android.backend.Tunnel.State;
import com.wireguard.android.util.SharedLibraryLoader;
import com.wireguard.config.Config;
import com.wireguard.config.InetEndpoint;
import com.wireguard.config.InetNetwork;
import com.wireguard.config.Peer;
import com.wireguard.crypto.Key;
import com.wireguard.crypto.KeyFormatException;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.backend.BackendException.Reason;
import org.amnezia.awg.backend.Tunnel.State;
import org.amnezia.awg.util.SharedLibraryLoader;
import org.amnezia.awg.config.Config;
import org.amnezia.awg.config.InetEndpoint;
import org.amnezia.awg.config.InetNetwork;
import org.amnezia.awg.config.Peer;
import org.amnezia.awg.crypto.Key;
import org.amnezia.awg.crypto.KeyFormatException;
import org.amnezia.awg.util.NonNullForAll;
import java.net.InetAddress;
import java.time.Instant;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -36,14 +35,16 @@ import java.util.concurrent.TimeoutException;
import androidx.annotation.Nullable;
import androidx.collection.ArraySet;
import static org.amnezia.awg.GoBackend.*;
/**
* Implementation of {@link Backend} that uses the wireguard-go userspace implementation to provide
* WireGuard tunnels.
* Implementation of {@link Backend} that uses the amneziawg-go userspace implementation to provide
* AmneziaWG tunnels.
*/
@NonNullForAll
public final class GoBackend implements Backend {
private static final int DNS_RESOLUTION_RETRIES = 10;
private static final String TAG = "WireGuard/GoBackend";
private static final String TAG = "AmneziaWG/GoBackend";
@Nullable private static AlwaysOnCallback alwaysOnCallback;
private static GhettoCompletableFuture<VpnService> vpnService = new GhettoCompletableFuture<>();
private final Context context;
@@ -71,17 +72,7 @@ public final class GoBackend implements Backend {
alwaysOnCallback = cb;
}
@Nullable private static native String wgGetConfig(int handle);
private static native int wgGetSocketV4(int handle);
private static native int wgGetSocketV6(int handle);
private static native void wgTurnOff(int handle);
private static native int wgTurnOn(String ifName, int tunFd, String settings);
private static native String wgVersion();
/**
* Method to get the names of running tunnels.
@@ -120,7 +111,7 @@ public final class GoBackend implements Backend {
final Statistics stats = new Statistics();
if (tunnel != currentTunnel || currentTunnelHandle == -1)
return stats;
final String config = wgGetConfig(currentTunnelHandle);
final String config = awgGetConfig(currentTunnelHandle);
if (config == null)
return stats;
Key key = null;
@@ -179,13 +170,13 @@ public final class GoBackend implements Backend {
}
/**
* Get the version of the underlying wireguard-go library.
* Get the version of the underlying amneziawg-go library.
*
* @return {@link String} value of the version of the wireguard-go library.
* @return {@link String} value of the version of the amneziawg-go library.
*/
@Override
public String getVersion() {
return wgVersion();
return awgVersion();
}
/**
@@ -275,7 +266,7 @@ public final class GoBackend implements Backend {
}
// Build config
final String goConfig = config.toWgUserspaceString();
final String goConfig = config.toAwgUserspaceString();
// Create the vpn tunnel with android API
final VpnService.Builder builder = service.getBuilder();
@@ -322,8 +313,8 @@ public final class GoBackend implements Backend {
try (final ParcelFileDescriptor tun = builder.establish()) {
if (tun == null)
throw new BackendException(Reason.TUN_CREATION_ERROR);
Log.d(TAG, "Go backend " + wgVersion());
currentTunnelHandle = wgTurnOn(tunnel.getName(), tun.detachFd(), goConfig);
Log.d(TAG, "Go backend " + awgVersion());
currentTunnelHandle = awgTurnOn(tunnel.getName(), tun.detachFd(), goConfig);
}
if (currentTunnelHandle < 0)
throw new BackendException(Reason.GO_ACTIVATION_ERROR_CODE, currentTunnelHandle);
@@ -331,8 +322,8 @@ public final class GoBackend implements Backend {
currentTunnel = tunnel;
currentConfig = config;
service.protect(wgGetSocketV4(currentTunnelHandle));
service.protect(wgGetSocketV6(currentTunnelHandle));
service.protect(awgGetSocketV4(currentTunnelHandle));
service.protect(awgGetSocketV6(currentTunnelHandle));
} else {
if (currentTunnelHandle == -1) {
Log.w(TAG, "Tunnel already down");
@@ -342,7 +333,7 @@ public final class GoBackend implements Backend {
currentTunnel = null;
currentTunnelHandle = -1;
currentConfig = null;
wgTurnOff(handleToClose);
awgTurnOff(handleToClose);
try {
vpnService.get(0, TimeUnit.NANOSECONDS).stopSelf();
} catch (final TimeoutException ignored) { }
@@ -410,7 +401,7 @@ public final class GoBackend implements Backend {
final Tunnel tunnel = owner.currentTunnel;
if (tunnel != null) {
if (owner.currentTunnelHandle != -1)
wgTurnOff(owner.currentTunnelHandle);
awgTurnOff(owner.currentTunnelHandle);
owner.currentTunnel = null;
owner.currentTunnelHandle = -1;
owner.currentConfig = null;

View File

@@ -3,16 +3,15 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.backend;
package org.amnezia.awg.backend;
import android.os.SystemClock;
import com.wireguard.crypto.Key;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.crypto.Key;
import org.amnezia.awg.util.NonNullForAll;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import androidx.annotation.Nullable;
@@ -31,12 +30,12 @@ public class Statistics {
/**
* Add a peer and its current stats to the internal map.
*
* @param key A WireGuard public key bound to a particular peer
* @param rxBytes The received traffic for the {@link com.wireguard.config.Peer} referenced by
* @param key An AmneziaWG public key bound to a particular peer
* @param rxBytes The received traffic for the {@link org.amnezia.config.Peer} referenced by
* the provided {@link Key}. This value is in bytes
* @param txBytes The transmitted traffic for the {@link com.wireguard.config.Peer} referenced by
* @param txBytes The transmitted traffic for the {@link org.amnezia.config.Peer} referenced by
* the provided {@link Key}. This value is in bytes.
* @param latestHandshake The timestamp of the latest handshake for the {@link com.wireguard.config.Peer}
* @param latestHandshake The timestamp of the latest handshake for the {@link org.amnezia.config.Peer}
* referenced by the provided {@link Key}. The value is in epoch milliseconds.
*/
void add(final Key key, final long rxBytes, final long txBytes, final long latestHandshake) {
@@ -54,9 +53,9 @@ public class Statistics {
}
/**
* Get the statistics for the {@link com.wireguard.config.Peer} referenced by the provided {@link Key}
* Get the statistics for the {@link org.amnezia.config.Peer} referenced by the provided {@link Key}
*
* @param peer A {@link Key} representing a {@link com.wireguard.config.Peer}.
* @param peer A {@link Key} representing a {@link org.amnezia.config.Peer}.
* @return a {@link PeerStats} representing various statistics about this peer.
*/
@Nullable
@@ -67,8 +66,8 @@ public class Statistics {
/**
* Get the list of peers being tracked by this instance.
*
* @return An array of {@link Key} instances representing WireGuard
* {@link com.wireguard.config.Peer}s
* @return An array of {@link Key} instances representing AmneziaWG
* {@link org.amnezia.config.Peer}s
*/
public Key[] peers() {
return stats.keySet().toArray(new Key[0]);

View File

@@ -3,14 +3,14 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.backend;
package org.amnezia.awg.backend;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.util.NonNullForAll;
import java.util.regex.Pattern;
/**
* Represents a WireGuard tunnel.
* Represents an AmneziaWG tunnel.
*/
@NonNullForAll

View File

@@ -3,9 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.config;
package org.amnezia.awg.config;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.util.NonNullForAll;
import java.util.Iterator;
import java.util.Optional;

View File

@@ -3,10 +3,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.config;
package org.amnezia.awg.config;
import com.wireguard.crypto.KeyFormatException;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.crypto.KeyFormatException;
import org.amnezia.awg.util.NonNullForAll;
import androidx.annotation.Nullable;
@@ -78,7 +78,16 @@ public class BadConfigException extends Exception {
PERSISTENT_KEEPALIVE("PersistentKeepalive"),
PRE_SHARED_KEY("PresharedKey"),
PRIVATE_KEY("PrivateKey"),
PUBLIC_KEY("PublicKey");
PUBLIC_KEY("PublicKey"),
JUNK_PACKET_COUNT("JunkPacketCount"),
JUNK_PACKET_MIN_SIZE("JunkPacketMinSize"),
JUNK_PACKET_MAX_SIZE("JunkPacketMaxSize"),
INIT_PACKET_JUNK_SIZE("InitPacketJunkSize"),
RESPONSE_PACKET_JUNK_SIZE("ResponsePacketJunkSize"),
INIT_PACKET_MAGIC_HEADER("InitPacketMagicHeader"),
RESPONSE_PACKET_MAGIC_HEADER("ResponsePacketMagicHeader"),
UNDERLOAD_PACKET_MAGIC_HEADER("UnderloadPacketMagicHeader"),
TRANSPORT_PACKET_MAGIC_HEADER("TransportPacketMagicHeader");
private final String name;

View File

@@ -3,12 +3,12 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.config;
package org.amnezia.awg.config;
import com.wireguard.config.BadConfigException.Location;
import com.wireguard.config.BadConfigException.Reason;
import com.wireguard.config.BadConfigException.Section;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.config.BadConfigException.Location;
import org.amnezia.awg.config.BadConfigException.Reason;
import org.amnezia.awg.config.BadConfigException.Section;
import org.amnezia.awg.util.NonNullForAll;
import java.io.BufferedReader;
import java.io.IOException;
@@ -23,7 +23,7 @@ import java.util.Objects;
import androidx.annotation.Nullable;
/**
* Represents the contents of a wg-quick configuration file, made up of one or more "Interface"
* Represents the contents of a awg-quick configuration file, made up of one or more "Interface"
* sections (combined together), and zero or more "Peer" sections (treated individually).
* <p>
* Instances of this class are immutable.
@@ -44,7 +44,7 @@ public final class Config {
* {@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
* @param stream a stream of UTF-8 text that is interpreted as an AmneziaWG configuration
* @return a {@code Config} instance representing the supplied configuration
*/
public static Config parse(final InputStream stream)
@@ -57,7 +57,7 @@ public final class Config {
* {@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
* @param reader a BufferedReader of UTF-8 text that is interpreted as an AmneziaWG configuration
* @return a {@code Config} instance representing the supplied configuration
*/
public static Config parse(final BufferedReader reader)
@@ -155,30 +155,30 @@ public final class Config {
}
/**
* Converts the {@code Config} into a string suitable for use as a {@code wg-quick}
* Converts the {@code Config} into a string suitable for use as a {@code awg-quick}
* configuration file.
*
* @return the {@code Config} represented as one [Interface] and zero or more [Peer] sections
*/
public String toWgQuickString() {
public String toAwgQuickString() {
final StringBuilder sb = new StringBuilder();
sb.append("[Interface]\n").append(interfaze.toWgQuickString());
sb.append("[Interface]\n").append(interfaze.toAwgQuickString());
for (final Peer peer : peers)
sb.append("\n[Peer]\n").append(peer.toWgQuickString());
sb.append("\n[Peer]\n").append(peer.toAwgQuickString());
return sb.toString();
}
/**
* Serializes the {@code Config} for use with the WireGuard cross-platform userspace API.
* Serializes the {@code Config} for use with the AmneziaWG cross-platform userspace API.
*
* @return the {@code Config} represented as a series of "key=value" lines
*/
public String toWgUserspaceString() {
public String toAwgUserspaceString() {
final StringBuilder sb = new StringBuilder();
sb.append(interfaze.toWgUserspaceString());
sb.append(interfaze.toAwgUserspaceString());
sb.append("replace_peers=true\n");
for (final Peer peer : peers)
sb.append(peer.toWgUserspaceString());
sb.append(peer.toAwgUserspaceString());
return sb.toString();
}

View File

@@ -3,9 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.config;
package org.amnezia.awg.config;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.util.NonNullForAll;
import java.lang.reflect.Method;
import java.net.Inet4Address;

View File

@@ -3,9 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.config;
package org.amnezia.awg.config;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.util.NonNullForAll;
import java.net.Inet4Address;
import java.net.InetAddress;
@@ -21,7 +21,7 @@ import androidx.annotation.Nullable;
/**
* An external endpoint (host and port) used to connect to a WireGuard {@link Peer}.
* An external endpoint (host and port) used to connect to an AmneziaWG {@link Peer}.
* <p>
* Instances of this class are externally immutable.
*/
@@ -48,7 +48,7 @@ public final class InetEndpoint {
throw new ParseException(InetEndpoint.class, endpoint, "Forbidden characters");
final URI uri;
try {
uri = new URI("wg://" + endpoint);
uri = new URI("awg://" + endpoint);
} catch (final URISyntaxException e) {
throw new ParseException(InetEndpoint.class, endpoint, e);
}

View File

@@ -3,9 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.config;
package org.amnezia.awg.config;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.util.NonNullForAll;
import java.net.Inet4Address;
import java.net.InetAddress;

View File

@@ -0,0 +1,750 @@
/*
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package org.amnezia.awg.config;
import org.amnezia.awg.config.BadConfigException.Location;
import org.amnezia.awg.config.BadConfigException.Reason;
import org.amnezia.awg.config.BadConfigException.Section;
import org.amnezia.awg.crypto.Key;
import org.amnezia.awg.crypto.KeyFormatException;
import org.amnezia.awg.crypto.KeyPair;
import org.amnezia.awg.util.NonNullForAll;
import java.net.InetAddress;
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.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import androidx.annotation.Nullable;
/**
* Represents the configuration for an AmneziaWG interface (an [Interface] block). Interfaces must
* have a private key (used to initialize a {@code KeyPair}), and may optionally have several other
* attributes.
* <p>
* Instances of this class are immutable.
*/
@NonNullForAll
public final class Interface {
private static final int MAX_UDP_PORT = 65535;
private static final int MIN_UDP_PORT = 0;
private final Set<InetNetwork> addresses;
private final Set<InetAddress> dnsServers;
private final Set<String> dnsSearchDomains;
private final Set<String> excludedApplications;
private final Set<String> includedApplications;
private final KeyPair keyPair;
private final Optional<Integer> listenPort;
private final Optional<Integer> mtu;
private final Optional<Integer> junkPacketCount;
private final Optional<Integer> junkPacketMinSize;
private final Optional<Integer> junkPacketMaxSize;
private final Optional<Integer> initPacketJunkSize;
private final Optional<Integer> responsePacketJunkSize;
private final Optional<Long> initPacketMagicHeader;
private final Optional<Long> responsePacketMagicHeader;
private final Optional<Long> underloadPacketMagicHeader;
private final Optional<Long> transportPacketMagicHeader;
private Interface(final Builder builder) {
// 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));
dnsSearchDomains = Collections.unmodifiableSet(new LinkedHashSet<>(builder.dnsSearchDomains));
excludedApplications = Collections.unmodifiableSet(new LinkedHashSet<>(builder.excludedApplications));
includedApplications = Collections.unmodifiableSet(new LinkedHashSet<>(builder.includedApplications));
keyPair = Objects.requireNonNull(builder.keyPair, "Interfaces must have a private key");
listenPort = builder.listenPort;
mtu = builder.mtu;
junkPacketCount = builder.junkPacketCount;
junkPacketMinSize = builder.junkPacketMinSize;
junkPacketMaxSize = builder.junkPacketMaxSize;
initPacketJunkSize = builder.initPacketJunkSize;
responsePacketJunkSize = builder.responsePacketJunkSize;
initPacketMagicHeader = builder.initPacketMagicHeader;
responsePacketMagicHeader = builder.responsePacketMagicHeader;
underloadPacketMagicHeader = builder.underloadPacketMagicHeader;
transportPacketMagicHeader = builder.transportPacketMagicHeader;
}
/**
* Parses an series of "KEY = VALUE" lines into an {@code Interface}. Throws
* {@link ParseException} if the input is not well-formed or contains unknown attributes.
*
* @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 BadConfigException {
final Builder builder = new Builder();
for (final CharSequence line : lines) {
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());
break;
case "dns":
builder.parseDnsServers(attribute.getValue());
break;
case "excludedapplications":
builder.parseExcludedApplications(attribute.getValue());
break;
case "includedapplications":
builder.parseIncludedApplications(attribute.getValue());
break;
case "listenport":
builder.parseListenPort(attribute.getValue());
break;
case "mtu":
builder.parseMtu(attribute.getValue());
break;
case "privatekey":
builder.parsePrivateKey(attribute.getValue());
break;
case "jc":
builder.parseJunkPacketCount(attribute.getValue());
break;
case "jmin":
builder.parseJunkPacketMinSize(attribute.getValue());
break;
case "jmax":
builder.parseJunkPacketMaxSize(attribute.getValue());
break;
case "s1":
builder.parseInitPacketJunkSize(attribute.getValue());
break;
case "s2":
builder.parseResponsePacketJunkSize(attribute.getValue());
break;
case "h1":
builder.parseInitPacketMagicHeader(attribute.getValue());
break;
case "h2":
builder.parseResponsePacketMagicHeader(attribute.getValue());
break;
case "h3":
builder.parseUnderloadPacketMagicHeader(attribute.getValue());
break;
case "h4":
builder.parseTransportPacketMagicHeader(attribute.getValue());
break;
default:
throw new BadConfigException(Section.INTERFACE, Location.TOP_LEVEL,
Reason.UNKNOWN_ATTRIBUTE, attribute.getKey());
}
}
return builder.build();
}
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof Interface))
return false;
final Interface other = (Interface) obj;
return addresses.equals(other.addresses)
&& dnsServers.equals(other.dnsServers)
&& dnsSearchDomains.equals(other.dnsSearchDomains)
&& excludedApplications.equals(other.excludedApplications)
&& includedApplications.equals(other.includedApplications)
&& keyPair.equals(other.keyPair)
&& listenPort.equals(other.listenPort)
&& mtu.equals(other.mtu)
&& junkPacketCount.equals(other.junkPacketCount)
&& junkPacketMinSize.equals(other.junkPacketMinSize)
&& junkPacketMaxSize.equals(other.junkPacketMaxSize)
&& initPacketJunkSize.equals(other.initPacketJunkSize)
&& responsePacketJunkSize.equals(other.responsePacketJunkSize)
&& initPacketMagicHeader.equals(other.initPacketMagicHeader)
&& responsePacketMagicHeader.equals(other.responsePacketMagicHeader)
&& underloadPacketMagicHeader.equals(other.underloadPacketMagicHeader)
&& transportPacketMagicHeader.equals(other.transportPacketMagicHeader);
}
/**
* Returns the set of IP addresses assigned to the interface.
*
* @return a set of {@link InetNetwork}s
*/
public Set<InetNetwork> getAddresses() {
// The collection is already immutable.
return addresses;
}
/**
* Returns the set of DNS servers associated with the interface.
*
* @return a set of {@link InetAddress}es
*/
public Set<InetAddress> getDnsServers() {
// The collection is already immutable.
return dnsServers;
}
/**
* Returns the set of DNS search domains associated with the interface.
*
* @return a set of strings
*/
public Set<String> getDnsSearchDomains() {
// The collection is already immutable.
return dnsSearchDomains;
}
/**
* Returns the set of applications excluded from using the interface.
*
* @return a set of package names
*/
public Set<String> getExcludedApplications() {
// The collection is already immutable.
return excludedApplications;
}
/**
* Returns the set of applications included exclusively for using the interface.
*
* @return a set of package names
*/
public Set<String> getIncludedApplications() {
// The collection is already immutable.
return includedApplications;
}
/**
* Returns the public/private key pair used by the interface.
*
* @return a key pair
*/
public KeyPair getKeyPair() {
return keyPair;
}
/**
* Returns the UDP port number that the AmneziaWG interface will listen on.
*
* @return a UDP port number, or {@code Optional.empty()} if none is configured
*/
public Optional<Integer> getListenPort() {
return listenPort;
}
/**
* Returns the MTU used for the AmneziaWG interface.
*
* @return the MTU, or {@code Optional.empty()} if none is configured
*/
public Optional<Integer> getMtu() {
return mtu;
}
/**
* Returns the junkPacketCount used for the AmneziaWG interface.
*
* @return the junkPacketCount, or {@code Optional.empty()} if none is configured
*/
public Optional<Integer> getJunkPacketCount() {
return junkPacketCount;
}
/**
* Returns the junkPacketMinSize used for the AmneziaWG interface.
*
* @return the junkPacketMinSize, or {@code Optional.empty()} if none is configured
*/
public Optional<Integer> getJunkPacketMinSize() {
return junkPacketMinSize;
}
/**
* Returns the junkPacketMaxSize used for the AmneziaWG interface.
*
* @return the junkPacketMaxSize, or {@code Optional.empty()} if none is configured
*/
public Optional<Integer> getJunkPacketMaxSize() {
return junkPacketMaxSize;
}
/**
* Returns the initPacketJunkSize used for the AmneziaWG interface.
*
* @return the initPacketJunkSize, or {@code Optional.empty()} if none is configured
*/
public Optional<Integer> getInitPacketJunkSize() {
return initPacketJunkSize;
}
/**
* Returns the responsePacketJunkSize used for the AmneziaWG interface.
*
* @return the responsePacketJunkSize, or {@code Optional.empty()} if none is configured
*/
public Optional<Integer> getResponsePacketJunkSize() {
return responsePacketJunkSize;
}
/**
* Returns the initPacketMagicHeader used for the AmneziaWG interface.
*
* @return the initPacketMagicHeader, or {@code Optional.empty()} if none is configured
*/
public Optional<Long> getInitPacketMagicHeader() {
return initPacketMagicHeader;
}
/**
* Returns the responsePacketMagicHeader used for the AmneziaWG interface.
*
* @return the responsePacketMagicHeader, or {@code Optional.empty()} if none is configured
*/
public Optional<Long> getResponsePacketMagicHeader() {
return responsePacketMagicHeader;
}
/**
* Returns the underloadPacketMagicHeader used for the AmneziaWG interface.
*
* @return the underloadPacketMagicHeader, or {@code Optional.empty()} if none is configured
*/
public Optional<Long> getUnderloadPacketMagicHeader() {
return underloadPacketMagicHeader;
}
/**
* Returns the transportPacketMagicHeader used for the AmneziaWG interface.
*
* @return the transportPacketMagicHeader, or {@code Optional.empty()} if none is configured
*/
public Optional<Long> getTransportPacketMagicHeader() {
return transportPacketMagicHeader;
}
@Override
public int hashCode() {
int hash = 1;
hash = 31 * hash + addresses.hashCode();
hash = 31 * hash + dnsServers.hashCode();
hash = 31 * hash + excludedApplications.hashCode();
hash = 31 * hash + includedApplications.hashCode();
hash = 31 * hash + keyPair.hashCode();
hash = 31 * hash + listenPort.hashCode();
hash = 31 * hash + mtu.hashCode();
hash = 31 * hash + junkPacketCount.hashCode();
hash = 31 * hash + junkPacketMinSize.hashCode();
hash = 31 * hash + junkPacketMaxSize.hashCode();
hash = 31 * hash + initPacketJunkSize.hashCode();
hash = 31 * hash + responsePacketJunkSize.hashCode();
hash = 31 * hash + initPacketMagicHeader.hashCode();
hash = 31 * hash + responsePacketMagicHeader.hashCode();
hash = 31 * hash + underloadPacketMagicHeader.hashCode();
hash = 31 * hash + transportPacketMagicHeader.hashCode();
return hash;
}
/**
* Converts the {@code Interface} into a string suitable for debugging purposes. The {@code
* Interface} is identified by its public key and (if set) the port used for its UDP socket.
*
* @return A concise single-line identifier for the {@code Interface}
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("(Interface ");
sb.append(keyPair.getPublicKey().toBase64());
listenPort.ifPresent(lp -> sb.append(" @").append(lp));
sb.append(')');
return sb.toString();
}
/**
* Converts the {@code Interface} into a string suitable for inclusion in a {@code awg-quick}
* configuration file.
*
* @return The {@code Interface} represented as a series of "Key = Value" lines
*/
public String toAwgQuickString() {
final StringBuilder sb = new StringBuilder();
if (!addresses.isEmpty())
sb.append("Address = ").append(Attribute.join(addresses)).append('\n');
if (!dnsServers.isEmpty()) {
final List<String> dnsServerStrings = dnsServers.stream().map(InetAddress::getHostAddress).collect(Collectors.toList());
dnsServerStrings.addAll(dnsSearchDomains);
sb.append("DNS = ").append(Attribute.join(dnsServerStrings)).append('\n');
}
if (!excludedApplications.isEmpty())
sb.append("ExcludedApplications = ").append(Attribute.join(excludedApplications)).append('\n');
if (!includedApplications.isEmpty())
sb.append("IncludedApplications = ").append(Attribute.join(includedApplications)).append('\n');
listenPort.ifPresent(lp -> sb.append("ListenPort = ").append(lp).append('\n'));
mtu.ifPresent(m -> sb.append("MTU = ").append(m).append('\n'));
junkPacketCount.ifPresent(jc -> sb.append("Jc = ").append(jc).append('\n'));
junkPacketMinSize.ifPresent(jmin -> sb.append("Jmin = ").append(jmin).append('\n'));
junkPacketMaxSize.ifPresent(jmax -> sb.append("Jmax = ").append(jmax).append('\n'));
initPacketJunkSize.ifPresent(s1 -> sb.append("S1 = ").append(s1).append('\n'));
responsePacketJunkSize.ifPresent(s2 -> sb.append("S2 = ").append(s2).append('\n'));
initPacketMagicHeader.ifPresent(h1 -> sb.append("H1 = ").append(h1).append('\n'));
responsePacketMagicHeader.ifPresent(h2 -> sb.append("H2 = ").append(h2).append('\n'));
underloadPacketMagicHeader.ifPresent(h3 -> sb.append("H3 = ").append(h3).append('\n'));
transportPacketMagicHeader.ifPresent(h4 -> sb.append("H4 = ").append(h4).append('\n'));
sb.append("PrivateKey = ").append(keyPair.getPrivateKey().toBase64()).append('\n');
return sb.toString();
}
/**
* Serializes the {@code Interface} for use with the AmneziaWG cross-platform userspace API.
* Note that not all attributes are included in this representation.
*
* @return the {@code Interface} represented as a series of "KEY=VALUE" lines
*/
public String toAwgUserspaceString() {
final StringBuilder sb = new StringBuilder();
sb.append("private_key=").append(keyPair.getPrivateKey().toHex()).append('\n');
listenPort.ifPresent(lp -> sb.append("listen_port=").append(lp).append('\n'));
junkPacketCount.ifPresent(jc -> sb.append("jc=").append(jc).append('\n'));
junkPacketMinSize.ifPresent(jmin -> sb.append("jmin=").append(jmin).append('\n'));
junkPacketMaxSize.ifPresent(jmax -> sb.append("jmax=").append(jmax).append('\n'));
initPacketJunkSize.ifPresent(s1 -> sb.append("s1=").append(s1).append('\n'));
responsePacketJunkSize.ifPresent(s2 -> sb.append("s2=").append(s2).append('\n'));
initPacketMagicHeader.ifPresent(h1 -> sb.append("h1=").append(h1).append('\n'));
responsePacketMagicHeader.ifPresent(h2 -> sb.append("h2=").append(h2).append('\n'));
underloadPacketMagicHeader.ifPresent(h3 -> sb.append("h3=").append(h3).append('\n'));
transportPacketMagicHeader.ifPresent(h4 -> sb.append("h4=").append(h4).append('\n'));
return sb.toString();
}
@SuppressWarnings("UnusedReturnValue")
public static final class Builder {
// Defaults to an empty set.
private final Set<InetNetwork> addresses = new LinkedHashSet<>();
// Defaults to an empty set.
private final Set<InetAddress> dnsServers = new LinkedHashSet<>();
// Defaults to an empty set.
private final Set<String> dnsSearchDomains = new LinkedHashSet<>();
// Defaults to an empty set.
private final Set<String> excludedApplications = new LinkedHashSet<>();
// Defaults to an empty set.
private final Set<String> includedApplications = new LinkedHashSet<>();
// No default; must be provided before building.
@Nullable private KeyPair keyPair;
// Defaults to not present.
private Optional<Integer> listenPort = Optional.empty();
// Defaults to not present.
private Optional<Integer> mtu = Optional.empty();
// Defaults to not present.
private Optional<Integer> junkPacketCount = Optional.empty();
// Defaults to not present.
private Optional<Integer> junkPacketMinSize = Optional.empty();
// Defaults to not present.
private Optional<Integer> junkPacketMaxSize = Optional.empty();
// Defaults to not present.
private Optional<Integer> initPacketJunkSize = Optional.empty();
// Defaults to not present.
private Optional<Integer> responsePacketJunkSize = Optional.empty();
// Defaults to not present.
private Optional<Long> initPacketMagicHeader = Optional.empty();
// Defaults to not present.
private Optional<Long> responsePacketMagicHeader = Optional.empty();
// Defaults to not present.
private Optional<Long> underloadPacketMagicHeader = Optional.empty();
// Defaults to not present.
private Optional<Long> transportPacketMagicHeader = Optional.empty();
public Builder addAddress(final InetNetwork address) {
addresses.add(address);
return this;
}
public Builder addAddresses(final Collection<InetNetwork> addresses) {
this.addresses.addAll(addresses);
return this;
}
public Builder addDnsServer(final InetAddress dnsServer) {
dnsServers.add(dnsServer);
return this;
}
public Builder addDnsServers(final Collection<? extends InetAddress> dnsServers) {
this.dnsServers.addAll(dnsServers);
return this;
}
public Builder addDnsSearchDomain(final String dnsSearchDomain) {
dnsSearchDomains.add(dnsSearchDomain);
return this;
}
public Builder addDnsSearchDomains(final Collection<String> dnsSearchDomains) {
this.dnsSearchDomains.addAll(dnsSearchDomains);
return this;
}
public Interface build() throws BadConfigException {
if (keyPair == null)
throw new BadConfigException(Section.INTERFACE, Location.PRIVATE_KEY,
Reason.MISSING_ATTRIBUTE, null);
if (!includedApplications.isEmpty() && !excludedApplications.isEmpty())
throw new BadConfigException(Section.INTERFACE, Location.INCLUDED_APPLICATIONS,
Reason.INVALID_KEY, null);
return new Interface(this);
}
public Builder excludeApplication(final String application) {
excludedApplications.add(application);
return this;
}
public Builder excludeApplications(final Collection<String> applications) {
excludedApplications.addAll(applications);
return this;
}
public Builder includeApplication(final String application) {
includedApplications.add(application);
return this;
}
public Builder includeApplications(final Collection<String> applications) {
includedApplications.addAll(applications);
return this;
}
public Builder parseAddresses(final CharSequence addresses) throws BadConfigException {
try {
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 BadConfigException {
try {
for (final String dnsServer : Attribute.split(dnsServers)) {
try {
addDnsServer(InetAddresses.parse(dnsServer));
} catch (final ParseException e) {
if (e.getParsingClass() != InetAddress.class || !InetAddresses.isHostname(dnsServer))
throw e;
addDnsSearchDomain(dnsServer);
}
}
return this;
} catch (final ParseException e) {
throw new BadConfigException(Section.INTERFACE, Location.DNS, e);
}
}
public Builder parseExcludedApplications(final CharSequence apps) {
return excludeApplications(List.of(Attribute.split(apps)));
}
public Builder parseIncludedApplications(final CharSequence apps) {
return includeApplications(List.of(Attribute.split(apps)));
}
public Builder parseListenPort(final String listenPort) throws BadConfigException {
try {
return setListenPort(Integer.parseInt(listenPort));
} catch (final NumberFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.LISTEN_PORT, listenPort, e);
}
}
public Builder parseMtu(final String mtu) throws BadConfigException {
try {
return setMtu(Integer.parseInt(mtu));
} catch (final NumberFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.MTU, mtu, e);
}
}
public Builder parseJunkPacketCount(final String junkPacketCount) throws BadConfigException {
try {
return setJunkPacketCount(Integer.parseInt(junkPacketCount));
} catch (final NumberFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.JUNK_PACKET_COUNT, junkPacketCount, e);
}
}
public Builder parseJunkPacketMinSize(final String junkPacketMinSize) throws BadConfigException {
try {
return setJunkPacketMinSize(Integer.parseInt(junkPacketMinSize));
} catch (final NumberFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.JUNK_PACKET_MIN_SIZE, junkPacketMinSize, e);
}
}
public Builder parseJunkPacketMaxSize(final String junkPacketMaxSize) throws BadConfigException {
try {
return setJunkPacketMaxSize(Integer.parseInt(junkPacketMaxSize));
} catch (final NumberFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.JUNK_PACKET_MAX_SIZE, junkPacketMaxSize, e);
}
}
public Builder parseInitPacketJunkSize(final String initPacketJunkSize) throws BadConfigException {
try {
return setInitPacketJunkSize(Integer.parseInt(initPacketJunkSize));
} catch (final NumberFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.INIT_PACKET_JUNK_SIZE, initPacketJunkSize, e);
}
}
public Builder parseResponsePacketJunkSize(final String responsePacketJunkSize) throws BadConfigException {
try {
return setResponsePacketJunkSize(Integer.parseInt(responsePacketJunkSize));
} catch (final NumberFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.RESPONSE_PACKET_JUNK_SIZE, responsePacketJunkSize, e);
}
}
public Builder parseInitPacketMagicHeader(final String initPacketMagicHeader) throws BadConfigException {
try {
return setInitPacketMagicHeader(Long.parseLong(initPacketMagicHeader));
} catch (final NumberFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.INIT_PACKET_MAGIC_HEADER, initPacketMagicHeader, e);
}
}
public Builder parseResponsePacketMagicHeader(final String responsePacketMagicHeader) throws BadConfigException {
try {
return setResponsePacketMagicHeader(Long.parseLong(responsePacketMagicHeader));
} catch (final NumberFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.RESPONSE_PACKET_MAGIC_HEADER, responsePacketMagicHeader, e);
}
}
public Builder parseUnderloadPacketMagicHeader(final String underloadPacketMagicHeader) throws BadConfigException {
try {
return setUnderloadPacketMagicHeader(Long.parseLong(underloadPacketMagicHeader));
} catch (final NumberFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.UNDERLOAD_PACKET_MAGIC_HEADER, underloadPacketMagicHeader, e);
}
}
public Builder parseTransportPacketMagicHeader(final String transportPacketMagicHeader) throws BadConfigException {
try {
return setTransportPacketMagicHeader(Long.parseLong(transportPacketMagicHeader));
} catch (final NumberFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.TRANSPORT_PACKET_MAGIC_HEADER, transportPacketMagicHeader, e);
}
}
public Builder parsePrivateKey(final String privateKey) throws BadConfigException {
try {
return setKeyPair(new KeyPair(Key.fromBase64(privateKey)));
} catch (final KeyFormatException e) {
throw new BadConfigException(Section.INTERFACE, Location.PRIVATE_KEY, e);
}
}
public Builder setKeyPair(final KeyPair keyPair) {
this.keyPair = keyPair;
return this;
}
public Builder setListenPort(final int listenPort) throws BadConfigException {
if (listenPort < MIN_UDP_PORT || listenPort > MAX_UDP_PORT)
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) throws BadConfigException {
if (mtu < 0)
throw new BadConfigException(Section.INTERFACE, Location.MTU,
Reason.INVALID_VALUE, String.valueOf(mtu));
this.mtu = mtu == 0 ? Optional.empty() : Optional.of(mtu);
return this;
}
public Builder setJunkPacketCount(final int junkPacketCount) throws BadConfigException {
if (junkPacketCount < 0)
throw new BadConfigException(Section.INTERFACE, Location.JUNK_PACKET_COUNT,
Reason.INVALID_VALUE, String.valueOf(junkPacketCount));
this.junkPacketCount = junkPacketCount == 0 ? Optional.empty() : Optional.of(junkPacketCount);
return this;
}
public Builder setJunkPacketMinSize(final int junkPacketMinSize) throws BadConfigException {
if (junkPacketMinSize < 0)
throw new BadConfigException(Section.INTERFACE, Location.JUNK_PACKET_MIN_SIZE,
Reason.INVALID_VALUE, String.valueOf(junkPacketMinSize));
this.junkPacketMinSize = junkPacketMinSize == 0 ? Optional.empty() : Optional.of(junkPacketMinSize);
return this;
}
public Builder setJunkPacketMaxSize(final int junkPacketMaxSize) throws BadConfigException {
if (junkPacketMaxSize < 0)
throw new BadConfigException(Section.INTERFACE, Location.JUNK_PACKET_MAX_SIZE,
Reason.INVALID_VALUE, String.valueOf(junkPacketMaxSize));
this.junkPacketMaxSize = junkPacketMaxSize == 0 ? Optional.empty() : Optional.of(junkPacketMaxSize);
return this;
}
public Builder setInitPacketJunkSize(final int initPacketJunkSize) throws BadConfigException {
if (initPacketJunkSize < 0)
throw new BadConfigException(Section.INTERFACE, Location.INIT_PACKET_JUNK_SIZE,
Reason.INVALID_VALUE, String.valueOf(initPacketJunkSize));
this.initPacketJunkSize = initPacketJunkSize == 0 ? Optional.empty() : Optional.of(initPacketJunkSize);
return this;
}
public Builder setResponsePacketJunkSize(final int responsePacketJunkSize) throws BadConfigException {
if (responsePacketJunkSize < 0)
throw new BadConfigException(Section.INTERFACE, Location.RESPONSE_PACKET_JUNK_SIZE,
Reason.INVALID_VALUE, String.valueOf(responsePacketJunkSize));
this.responsePacketJunkSize = responsePacketJunkSize == 0 ? Optional.empty() : Optional.of(responsePacketJunkSize);
return this;
}
public Builder setInitPacketMagicHeader(final long initPacketMagicHeader) throws BadConfigException {
if (initPacketMagicHeader < 0)
throw new BadConfigException(Section.INTERFACE, Location.INIT_PACKET_MAGIC_HEADER,
Reason.INVALID_VALUE, String.valueOf(initPacketMagicHeader));
this.initPacketMagicHeader = initPacketMagicHeader == 0 ? Optional.empty() : Optional.of(initPacketMagicHeader);
return this;
}
public Builder setResponsePacketMagicHeader(final long responsePacketMagicHeader) throws BadConfigException {
if (responsePacketMagicHeader < 0)
throw new BadConfigException(Section.INTERFACE, Location.RESPONSE_PACKET_MAGIC_HEADER,
Reason.INVALID_VALUE, String.valueOf(responsePacketMagicHeader));
this.responsePacketMagicHeader = responsePacketMagicHeader == 0 ? Optional.empty() : Optional.of(responsePacketMagicHeader);
return this;
}
public Builder setUnderloadPacketMagicHeader(final long underloadPacketMagicHeader) throws BadConfigException {
if (underloadPacketMagicHeader < 0)
throw new BadConfigException(Section.INTERFACE, Location.UNDERLOAD_PACKET_MAGIC_HEADER,
Reason.INVALID_VALUE, String.valueOf(underloadPacketMagicHeader));
this.underloadPacketMagicHeader = underloadPacketMagicHeader == 0 ? Optional.empty() : Optional.of(underloadPacketMagicHeader);
return this;
}
public Builder setTransportPacketMagicHeader(final long transportPacketMagicHeader) throws BadConfigException {
if (transportPacketMagicHeader < 0)
throw new BadConfigException(Section.INTERFACE, Location.TRANSPORT_PACKET_MAGIC_HEADER,
Reason.INVALID_VALUE, String.valueOf(transportPacketMagicHeader));
this.transportPacketMagicHeader = transportPacketMagicHeader == 0 ? Optional.empty() : Optional.of(transportPacketMagicHeader);
return this;
}
}
}

View File

@@ -3,9 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.config;
package org.amnezia.awg.config;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.util.NonNullForAll;
import androidx.annotation.Nullable;

View File

@@ -3,14 +3,14 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.config;
package org.amnezia.awg.config;
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.util.NonNullForAll;
import org.amnezia.awg.config.BadConfigException.Location;
import org.amnezia.awg.config.BadConfigException.Reason;
import org.amnezia.awg.config.BadConfigException.Section;
import org.amnezia.awg.crypto.Key;
import org.amnezia.awg.crypto.KeyFormatException;
import org.amnezia.awg.util.NonNullForAll;
import java.util.Collection;
import java.util.Collections;
@@ -23,7 +23,7 @@ import java.util.Set;
import androidx.annotation.Nullable;
/**
* Represents the configuration for a WireGuard peer (a [Peer] block). Peers must have a public key,
* Represents the configuration for an AmneziaWG peer (a [Peer] block). Peers must have a public key,
* and may optionally have several other attributes.
* <p>
* Instances of this class are immutable.
@@ -168,12 +168,12 @@ public final class Peer {
}
/**
* Converts the {@code Peer} into a string suitable for inclusion in a {@code wg-quick}
* Converts the {@code Peer} into a string suitable for inclusion in a {@code awg-quick}
* configuration file.
*
* @return the {@code Peer} represented as a series of "Key = Value" lines
*/
public String toWgQuickString() {
public String toAwgQuickString() {
final StringBuilder sb = new StringBuilder();
if (!allowedIps.isEmpty())
sb.append("AllowedIPs = ").append(Attribute.join(allowedIps)).append('\n');
@@ -185,12 +185,12 @@ public final class Peer {
}
/**
* Serializes the {@code Peer} for use with the WireGuard cross-platform userspace API. Note
* Serializes the {@code Peer} for use with the AmneziaWG cross-platform userspace API. Note
* that not all attributes are included in this representation.
*
* @return the {@code Peer} represented as a series of "key=value" lines
*/
public String toWgUserspaceString() {
public String toAwgUserspaceString() {
final StringBuilder sb = new StringBuilder();
// The order here is important: public_key signifies the beginning of a new peer.
sb.append("public_key=").append(publicKey.toHex()).append('\n');
@@ -204,7 +204,7 @@ public final class Peer {
@SuppressWarnings("UnusedReturnValue")
public static final class Builder {
// See wg(8)
// See awg(8)
private static final int MAX_PERSISTENT_KEEPALIVE = 65535;
// Defaults to an empty set.

View File

@@ -4,9 +4,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.crypto;
package org.amnezia.awg.crypto;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.util.NonNullForAll;
import java.util.Arrays;
@@ -15,7 +15,7 @@ import androidx.annotation.Nullable;
/**
* Implementation of Curve25519 ECDH.
* <p>
* This implementation was imported to WireGuard from noise-java:
* This implementation was imported to AmneziaWG from noise-java:
* https://github.com/rweather/noise-java
* <p>
* This implementation is based on that from arduinolibs:

View File

@@ -3,17 +3,17 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.crypto;
package org.amnezia.awg.crypto;
import com.wireguard.crypto.KeyFormatException.Type;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.crypto.KeyFormatException.Type;
import org.amnezia.awg.util.NonNullForAll;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;
/**
* Represents a WireGuard public or private key. This class uses specialized constant-time base64
* Represents an AmneziaWG public or private key. This class uses specialized constant-time base64
* and hexadecimal codec implementations that resist side-channel attacks.
* <p>
* Instances of this class are immutable.
@@ -83,10 +83,10 @@ public final class Key {
}
/**
* Decodes a WireGuard public or private key from its base64 string representation. This
* Decodes an AmneziaWG public or private key from its base64 string representation. This
* function throws a {@link KeyFormatException} if the source string is not well-formed.
*
* @param str the base64 string representation of a WireGuard key
* @param str the base64 string representation of an AmneziaWG key
* @return the decoded key encapsulated in an immutable container
*/
public static Key fromBase64(final String str) throws KeyFormatException {
@@ -120,10 +120,10 @@ public final class Key {
}
/**
* Wraps a WireGuard public or private key in an immutable container. This function throws a
* Wraps an AmneziaWG public or private key in an immutable container. This function throws a
* {@link KeyFormatException} if the source data is not the correct length.
*
* @param bytes an array of bytes containing a WireGuard key in binary format
* @param bytes an array of bytes containing an AmneziaWG key in binary format
* @return the key encapsulated in an immutable container
*/
public static Key fromBytes(final byte[] bytes) throws KeyFormatException {
@@ -133,10 +133,10 @@ public final class Key {
}
/**
* Decodes a WireGuard public or private key from its hexadecimal string representation. This
* Decodes an AmneziaWG public or private key from its hexadecimal string representation. This
* function throws a {@link KeyFormatException} if the source string is not well-formed.
*
* @param str the hexadecimal string representation of a WireGuard key
* @param str the hexadecimal string representation of an AmneziaWG key
* @return the decoded key encapsulated in an immutable container
*/
public static Key fromHex(final String str) throws KeyFormatException {
@@ -269,7 +269,7 @@ public final class Key {
}
/**
* The supported formats for encoding a WireGuard key.
* The supported formats for encoding an AmneziaWG key.
*/
public enum Format {
BASE64(44),

View File

@@ -3,9 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.crypto;
package org.amnezia.awg.crypto;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.util.NonNullForAll;
/**
* An exception thrown when attempting to parse an invalid key (too short, too long, or byte

View File

@@ -3,12 +3,12 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.crypto;
package org.amnezia.awg.crypto;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.util.NonNullForAll;
/**
* Represents a Curve25519 key pair as used by WireGuard.
* Represents a Curve25519 key pair as used by AmneziaWG.
* <p>
* Instances of this class are immutable.
*/

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.util;
package org.amnezia.awg.util;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -3,13 +3,13 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util;
package org.amnezia.awg.util;
import android.content.Context;
import android.util.Log;
import com.wireguard.android.util.RootShell.RootShellException.Reason;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.util.RootShell.RootShellException.Reason;
import org.amnezia.awg.util.NonNullForAll;
import java.io.BufferedReader;
import java.io.File;
@@ -29,7 +29,7 @@ import androidx.annotation.Nullable;
@NonNullForAll
public class RootShell {
private static final String SU = "su";
private static final String TAG = "WireGuard/RootShell";
private static final String TAG = "AmneziaWG/RootShell";
private final File localBinaryDir;
private final File localTemporaryDir;

View File

@@ -3,13 +3,13 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util;
package org.amnezia.awg.util;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.util.NonNullForAll;
import java.io.File;
import java.io.FileOutputStream;
@@ -27,7 +27,7 @@ import androidx.annotation.RestrictTo.Scope;
@NonNullForAll
@RestrictTo(Scope.LIBRARY_GROUP)
public final class SharedLibraryLoader {
private static final String TAG = "WireGuard/SharedLibraryLoader";
private static final String TAG = "AmneziaWG/SharedLibraryLoader";
private SharedLibraryLoader() {
}

View File

@@ -3,14 +3,14 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util;
package org.amnezia.awg.util;
import android.content.Context;
import android.system.OsConstants;
import android.util.Log;
import com.wireguard.android.util.RootShell.RootShellException;
import com.wireguard.util.NonNullForAll;
import org.amnezia.awg.util.RootShell.RootShellException;
import org.amnezia.awg.util.NonNullForAll;
import java.io.File;
import java.io.FileNotFoundException;
@@ -23,7 +23,7 @@ import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
/**
* Helper to install WireGuard tools to the system partition.
* Helper to install AmneziaWG tools to the system partition.
*/
@NonNullForAll
@@ -33,13 +33,13 @@ 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 = {"wg", "wg-quick"};
private static final String[] EXECUTABLES = {"awg", "awg-quick"};
private static final File[] INSTALL_DIRS = {
new File("/system/xbin"),
new File("/system/bin"),
};
@Nullable private static final File INSTALL_DIR = getInstallDir();
private static final String TAG = "WireGuard/ToolsInstaller";
private static final String TAG = "AmneziaWG/ToolsInstaller";
private final Context context;
private final File localBinaryDir;
@@ -100,7 +100,7 @@ public final class ToolsInstaller {
"Tools were already extracted into our private binary dir");
areToolsAvailable = true;
} catch (final IOException e) {
Log.e(TAG, "The wg and wg-quick tools are not available", e);
Log.e(TAG, "The awg and awg-quick tools are not available", e);
areToolsAvailable = false;
}
}
@@ -134,8 +134,8 @@ public final class ToolsInstaller {
@RestrictTo(Scope.LIBRARY_GROUP)
public int install() throws RootShellException, IOException {
if (!context.getPackageName().startsWith("com.wireguard."))
throw new SecurityException("The tools may only be installed system-wide from the main WireGuard app.");
if (!context.getPackageName().startsWith("org.amnezia."))
throw new SecurityException("The tools may only be installed system-wide from the main AmneziaWG app.");
return willInstallAsMagiskModule() ? installMagisk() : installSystem();
}
@@ -143,12 +143,12 @@ public final class ToolsInstaller {
extract();
final StringBuilder script = new StringBuilder("set -ex; ");
script.append("trap 'rm -rf /data/adb/modules/wireguard' INT TERM EXIT; ");
script.append(String.format("rm -rf /data/adb/modules/wireguard/; mkdir -p /data/adb/modules/wireguard%s; ", INSTALL_DIR));
script.append("printf 'id=wireguard\nname=WireGuard Command Line Tools\nversion=1.0\nversionCode=1\nauthor=zx2c4\ndescription=Command line tools for WireGuard\nminMagisk=1500\n' > /data/adb/modules/wireguard/module.prop; ");
script.append("touch /data/adb/modules/wireguard/auto_mount; ");
script.append("trap 'rm -rf /data/adb/modules/amneziawg' INT TERM EXIT; ");
script.append(String.format("rm -rf /data/adb/modules/amneziawg/; mkdir -p /data/adb/modules/amneziawg%s; ", INSTALL_DIR));
script.append("printf 'id=amneziawg\nname=AmneziaWG Command Line Tools\nversion=1.0\nversionCode=1\nauthor=amnezia\ndescription=Command line tools for AmneziaWG\nminMagisk=1500\n' > /data/adb/modules/amneziawg/module.prop; ");
script.append("touch /data/adb/modules/amneziawg/auto_mount; ");
for (final String name : EXECUTABLES) {
final File destination = new File("/data/adb/modules/wireguard" + INSTALL_DIR, name);
final File destination = new File("/data/adb/modules/amneziawg" + 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));
}

View File

@@ -3,11 +3,11 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.config;
package org.amnezia.awg.config;
import com.wireguard.config.BadConfigException.Location;
import com.wireguard.config.BadConfigException.Reason;
import com.wireguard.config.BadConfigException.Section;
import org.amnezia.awg.config.BadConfigException.Location;
import org.amnezia.awg.config.BadConfigException.Reason;
import org.amnezia.awg.config.BadConfigException.Section;
import org.junit.AfterClass;
import org.junit.BeforeClass;

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.config;
package org.amnezia.awg.config;
import org.junit.Test;

View File

@@ -9,7 +9,7 @@ add_link_options(LINKER:--build-id=none)
add_compile_options(-Wall -Werror)
add_executable(libwg-quick.so amneziawg-tools/src/wg-quick/android.c ndk-compat/compat.c)
target_compile_options(libwg-quick.so PUBLIC -std=gnu11 -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DWG_PACKAGE_NAME=\"${ANDROID_PACKAGE_NAME}\")
target_compile_options(libwg-quick.so PUBLIC -std=gnu11 -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DAWG_PACKAGE_NAME=\"${ANDROID_PACKAGE_NAME}\")
target_link_libraries(libwg-quick.so -ldl)
file(GLOB WG_SOURCES amneziawg-tools/src/*.c ndk-compat/compat.c)

View File

@@ -20,12 +20,12 @@ export GOARCH := $(NDK_GO_ARCH_MAP_$(ANDROID_ARCH_NAME))
export GOOS := android
export CGO_ENABLED := 1
GO_VERSION := 1.20.3
GO_VERSION := 1.21.3
GO_PLATFORM := $(shell uname -s | tr '[:upper:]' '[:lower:]')-$(NDK_GO_ARCH_MAP_$(shell uname -m))
GO_TARBALL := go$(GO_VERSION).$(GO_PLATFORM).tar.gz
GO_HASH_darwin-amd64 := c1e1161d6d859deb576e6cfabeb40e3d042ceb1c6f444f617c3c9d76269c3565
GO_HASH_darwin-arm64 := 86b0ed0f2b2df50fa8036eea875d1cf2d76cefdacf247c44639a1464b7e36b95
GO_HASH_linux-amd64 := 979694c2c25c735755bf26f4f45e19e64e4811d661dd07b8c010f7a8e18adfca
GO_HASH_darwin-amd64 := 27014fc69e301d7588a169ca239b3cc609f0aa1abf38528bf0d20d3b259211eb
GO_HASH_darwin-arm64 := 65302a7a9f7a4834932b3a7a14cb8be51beddda757b567a2f9e0cbd0d7b5a6ab
GO_HASH_linux-amd64 := 1241381b2843fae5a9707eec1f8fb2ef94d827990582c7c7c32f5bdfbfd420c8
default: $(DESTDIR)/libwg-go.so

View File

@@ -72,8 +72,8 @@ func init() {
}()
}
//export wgTurnOn
func wgTurnOn(interfaceName string, tunFd int32, settings string) int32 {
//export awgTurnOn
func awgTurnOn(interfaceName string, tunFd int32, settings string) int32 {
tag := cstring("AmneziaWG/" + interfaceName)
logger := &device.Logger{
Verbosef: AndroidLogger{level: C.ANDROID_LOG_DEBUG, tag: tag}.Printf,
@@ -146,8 +146,8 @@ func wgTurnOn(interfaceName string, tunFd int32, settings string) int32 {
return i
}
//export wgTurnOff
func wgTurnOff(tunnelHandle int32) {
//export awgTurnOff
func awgTurnOff(tunnelHandle int32) {
handle, ok := tunnelHandles[tunnelHandle]
if !ok {
return
@@ -159,8 +159,8 @@ func wgTurnOff(tunnelHandle int32) {
handle.device.Close()
}
//export wgGetSocketV4
func wgGetSocketV4(tunnelHandle int32) int32 {
//export awgGetSocketV4
func awgGetSocketV4(tunnelHandle int32) int32 {
handle, ok := tunnelHandles[tunnelHandle]
if !ok {
return -1
@@ -176,8 +176,8 @@ func wgGetSocketV4(tunnelHandle int32) int32 {
return int32(fd)
}
//export wgGetSocketV6
func wgGetSocketV6(tunnelHandle int32) int32 {
//export awgGetSocketV6
func awgGetSocketV6(tunnelHandle int32) int32 {
handle, ok := tunnelHandles[tunnelHandle]
if !ok {
return -1
@@ -193,8 +193,8 @@ func wgGetSocketV6(tunnelHandle int32) int32 {
return int32(fd)
}
//export wgGetConfig
func wgGetConfig(tunnelHandle int32) *C.char {
//export awgGetConfig
func awgGetConfig(tunnelHandle int32) *C.char {
handle, ok := tunnelHandles[tunnelHandle]
if !ok {
return nil
@@ -206,8 +206,8 @@ func wgGetConfig(tunnelHandle int32) *C.char {
return C.CString(settings)
}
//export wgVersion
func wgVersion() *C.char {
//export awgVersion
func awgVersion() *C.char {
info, ok := debug.ReadBuildInfo()
if !ok {
return C.CString("unknown")

View File

@@ -1,9 +1,9 @@
module github.com/amnezia-vpn/amneziawg-android
go 1.20
go 1.21
require (
github.com/amnezia-vpn/amneziawg-go v0.2.1
github.com/amnezia-vpn/amneziawg-go v0.2.5
golang.org/x/sys v0.17.0
)

View File

@@ -1,6 +1,7 @@
github.com/amnezia-vpn/amneziawg-go v0.2.1 h1:cYcHr/3PgtrQhwueE+qZh3i01ZO0jpgJMcwvsAwoKW8=
github.com/amnezia-vpn/amneziawg-go v0.2.1/go.mod h1:12g0XRbFeGbpXvuCmBOV21YxLWSFnUFJnwgrzyHBUyk=
github.com/amnezia-vpn/amneziawg-go v0.2.5 h1:V5uvzWhxyPLuhWeh52VPG4sAOU7TpUMwQbkofADtJdI=
github.com/amnezia-vpn/amneziawg-go v0.2.5/go.mod h1:12g0XRbFeGbpXvuCmBOV21YxLWSFnUFJnwgrzyHBUyk=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/tevino/abool/v2 v2.1.0 h1:7w+Vf9f/5gmKT4m4qkayb33/92M+Um45F2BkHOR+L/c=
github.com/tevino/abool/v2 v2.1.0/go.mod h1:+Lmlqk6bHDWHqN1cbxqhwEAwMPXgc8I1SDEamtseuXY=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
@@ -10,6 +11,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=

View File

@@ -8,20 +8,20 @@
#include <string.h>
struct go_string { const char *str; long n; };
extern int wgTurnOn(struct go_string ifname, int tun_fd, struct go_string settings);
extern void wgTurnOff(int handle);
extern int wgGetSocketV4(int handle);
extern int wgGetSocketV6(int handle);
extern char *wgGetConfig(int handle);
extern char *wgVersion();
extern int awgTurnOn(struct go_string ifname, int tun_fd, struct go_string settings);
extern void awgTurnOff(int handle);
extern int awgGetSocketV4(int handle);
extern int awgGetSocketV6(int handle);
extern char *awgGetConfig(int handle);
extern char *awgVersion();
JNIEXPORT jint JNICALL Java_org_amnezia_vpn_protocol_wireguard_GoBackend_wgTurnOn(JNIEnv *env, jclass c, jstring ifname, jint tun_fd, jstring settings)
JNIEXPORT jint JNICALL Java_org_amnezia_awg_GoBackend_awgTurnOn(JNIEnv *env, jclass c, jstring ifname, jint tun_fd, jstring settings)
{
const char *ifname_str = (*env)->GetStringUTFChars(env, ifname, 0);
size_t ifname_len = (*env)->GetStringUTFLength(env, ifname);
const char *settings_str = (*env)->GetStringUTFChars(env, settings, 0);
size_t settings_len = (*env)->GetStringUTFLength(env, settings);
int ret = wgTurnOn((struct go_string){
int ret = awgTurnOn((struct go_string){
.str = ifname_str,
.n = ifname_len
}, tun_fd, (struct go_string){
@@ -33,25 +33,25 @@ JNIEXPORT jint JNICALL Java_org_amnezia_vpn_protocol_wireguard_GoBackend_wgTurnO
return ret;
}
JNIEXPORT void JNICALL Java_org_amnezia_vpn_protocol_wireguard_GoBackend_wgTurnOff(JNIEnv *env, jclass c, jint handle)
JNIEXPORT void JNICALL Java_org_amnezia_awg_GoBackend_awgTurnOff(JNIEnv *env, jclass c, jint handle)
{
wgTurnOff(handle);
awgTurnOff(handle);
}
JNIEXPORT jint JNICALL Java_org_amnezia_vpn_protocol_wireguard_GoBackend_wgGetSocketV4(JNIEnv *env, jclass c, jint handle)
JNIEXPORT jint JNICALL Java_org_amnezia_awg_GoBackend_awgGetSocketV4(JNIEnv *env, jclass c, jint handle)
{
return wgGetSocketV4(handle);
return awgGetSocketV4(handle);
}
JNIEXPORT jint JNICALL Java_org_amnezia_vpn_protocol_wireguard_GoBackend_wgGetSocketV6(JNIEnv *env, jclass c, jint handle)
JNIEXPORT jint JNICALL Java_org_amnezia_awg_GoBackend_awgGetSocketV6(JNIEnv *env, jclass c, jint handle)
{
return wgGetSocketV6(handle);
return awgGetSocketV6(handle);
}
JNIEXPORT jstring JNICALL Java_org_amnezia_vpn_protocol_wireguard_GoBackend_wgGetConfig(JNIEnv *env, jclass c, jint handle)
JNIEXPORT jstring JNICALL Java_org_amnezia_awg_GoBackend_awgGetConfig(JNIEnv *env, jclass c, jint handle)
{
jstring ret;
char *config = wgGetConfig(handle);
char *config = awgGetConfig(handle);
if (!config)
return NULL;
ret = (*env)->NewStringUTF(env, config);
@@ -59,10 +59,10 @@ JNIEXPORT jstring JNICALL Java_org_amnezia_vpn_protocol_wireguard_GoBackend_wgGe
return ret;
}
JNIEXPORT jstring JNICALL Java_org_amnezia_vpn_protocol_wireguard_GoBackend_wgVersion(JNIEnv *env, jclass c)
JNIEXPORT jstring JNICALL Java_org_amnezia_awg_GoBackend_awgVersion(JNIEnv *env, jclass c)
{
jstring ret;
char *version = wgVersion();
char *version = awgVersion();
if (!version)
return NULL;
ret = (*env)->NewStringUTF(env, version);

View File

@@ -3,18 +3,18 @@
"names": [
{
"names": [
{ "name": "wg0" },
{ "name": "wg1" },
{ "name": "wg2" },
{ "name": "wg3" },
{ "name": "wg4" },
{ "name": "wg5" },
{ "name": "wg6" },
{ "name": "wg7" },
{ "name": "wg8" },
{ "name": "wg9" },
{ "name": "wg10" },
{ "name": "wg11" }
{ "name": "awg0" },
{ "name": "awg1" },
{ "name": "awg2" },
{ "name": "awg3" },
{ "name": "awg4" },
{ "name": "awg5" },
{ "name": "awg6" },
{ "name": "awg7" },
{ "name": "awg8" },
{ "name": "awg9" },
{ "name": "awg10" },
{ "name": "awg11" }
],
"checked": [
{ "checked": true },

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">WireGuard β</string>
<string name="app_name" translatable="false">AmneziaWG β</string>
</resources>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission
android:name="android.permission.REQUEST_INSTALL_PACKAGES"
tools:node="remove" />
</manifest>

View File

@@ -6,7 +6,9 @@
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission
android:name="android.permission.SYSTEM_ALERT_WINDOW"
android:minSdkVersion="34" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"
@@ -111,22 +113,14 @@
</intent-filter>
</receiver>
<receiver
android:name=".updater.Updater$AppUpdatedReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
<receiver
android:name=".model.TunnelManager$IntentReceiver"
android:exported="true"
android:permission="${applicationId}.permission.CONTROL_TUNNELS">
<intent-filter>
<action android:name="com.wireguard.android.action.REFRESH_TUNNEL_STATES" />
<action android:name="com.wireguard.android.action.SET_TUNNEL_UP" />
<action android:name="com.wireguard.android.action.SET_TUNNEL_DOWN" />
<action android:name="org.amnezia.awg.action.REFRESH_TUNNEL_STATES" />
<action android:name="org.amnezia.awg.action.SET_TUNNEL_UP" />
<action android:name="org.amnezia.awg.action.SET_TUNNEL_DOWN" />
</intent-filter>
</receiver>

View File

@@ -1,42 +0,0 @@
/*
* Copyright © 2017-2023 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.net.Uri
import android.util.AttributeSet
import android.widget.Toast
import androidx.preference.Preference
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.wireguard.android.R
import com.wireguard.android.updater.Updater
import com.wireguard.android.util.ErrorMessages
class DonatePreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
override fun getSummary() = context.getString(R.string.donate_summary)
override fun getTitle() = context.getString(R.string.donate_title)
override fun onClick() {
/* Google Play Store forbids links to our donation page. */
if (Updater.installerIsGooglePlay(context)) {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.donate_title)
.setMessage(R.string.donate_google_play_disappointment)
.show()
return
}
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.wireguard.com/donations/")
try {
context.startActivity(intent)
} catch (e: Throwable) {
Toast.makeText(context, ErrorMessages[e], Toast.LENGTH_SHORT).show()
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,173 +0,0 @@
/*
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.updater
import android.content.Intent
import android.net.Uri
import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.wireguard.android.R
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.QuantityFormatter
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.seconds
class SnackbarUpdateShower(private val fragment: Fragment) {
private var lastUserIntervention: Updater.Progress.NeedsUserIntervention? = null
private val intentLauncher = fragment.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
lastUserIntervention?.markAsDone()
}
private class SwapableSnackbar(fragment: Fragment, view: View, anchor: View?) {
private val actionSnackbar = makeSnackbar(fragment, view, anchor)
private val statusSnackbar = makeSnackbar(fragment, view, anchor)
private var showingAction: Boolean = false
private var showingStatus: Boolean = false
private fun makeSnackbar(fragment: Fragment, view: View, anchor: View?): Snackbar {
val snackbar = Snackbar.make(fragment.requireContext(), view, "", Snackbar.LENGTH_INDEFINITE)
if (anchor != null)
snackbar.anchorView = anchor
snackbar.setTextMaxLines(6)
snackbar.behavior = object : BaseTransientBottomBar.Behavior() {
override fun canSwipeDismissView(child: View): Boolean {
return false
}
}
snackbar.addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(snackbar: Snackbar?, @DismissEvent event: Int) {
super.onDismissed(snackbar, event)
if (event == DISMISS_EVENT_MANUAL || event == DISMISS_EVENT_ACTION ||
(snackbar == actionSnackbar && !showingAction) || (snackbar == statusSnackbar && !showingStatus)
)
return
fragment.lifecycleScope.launch {
delay(5.seconds)
snackbar?.show()
}
}
})
return snackbar
}
fun showAction(text: String, action: String, listener: View.OnClickListener) {
if (showingStatus) {
showingStatus = false
statusSnackbar.dismiss()
}
actionSnackbar.setText(text)
actionSnackbar.setAction(action, listener)
if (!showingAction) {
actionSnackbar.show()
showingAction = true
}
}
fun showText(text: String) {
if (showingAction) {
showingAction = false
actionSnackbar.dismiss()
}
statusSnackbar.setText(text)
if (!showingStatus) {
statusSnackbar.show()
showingStatus = true
}
}
fun dismiss() {
actionSnackbar.dismiss()
statusSnackbar.dismiss()
showingAction = false
showingStatus = false
}
}
fun attach(view: View, anchor: View?) {
val snackbar = SwapableSnackbar(fragment, view, anchor)
val context = fragment.requireContext()
Updater.state.onEach { progress ->
when (progress) {
is Updater.Progress.Complete ->
snackbar.dismiss()
is Updater.Progress.Available ->
snackbar.showAction(context.getString(R.string.updater_avalable), context.getString(R.string.updater_action)) {
progress.update()
}
is Updater.Progress.NeedsUserIntervention -> {
lastUserIntervention = progress
intentLauncher.launch(progress.intent)
}
is Updater.Progress.Installing ->
snackbar.showText(context.getString(R.string.updater_installing))
is Updater.Progress.Rechecking ->
snackbar.showText(context.getString(R.string.updater_rechecking))
is Updater.Progress.Downloading -> {
if (progress.bytesTotal != 0UL) {
snackbar.showText(
context.getString(
R.string.updater_download_progress,
QuantityFormatter.formatBytes(progress.bytesDownloaded.toLong()),
QuantityFormatter.formatBytes(progress.bytesTotal.toLong()),
progress.bytesDownloaded.toFloat() * 100.0 / progress.bytesTotal.toFloat()
)
)
} else {
snackbar.showText(
context.getString(
R.string.updater_download_progress_nototal,
QuantityFormatter.formatBytes(progress.bytesDownloaded.toLong())
)
)
}
}
is Updater.Progress.Failure -> {
snackbar.showText(context.getString(R.string.updater_failure, ErrorMessages[progress.error]))
delay(5.seconds)
progress.retry()
}
is Updater.Progress.Corrupt -> {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.updater_corrupt_title)
.setMessage(R.string.updater_corrupt_message)
.setPositiveButton(R.string.updater_corrupt_navigate) { _, _ ->
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(progress.downloadUrl)
try {
context.startActivity(intent)
} catch (e: Throwable) {
Toast.makeText(context, ErrorMessages[e], Toast.LENGTH_SHORT).show()
}
}.setCancelable(false).setOnDismissListener {
val intent = Intent(Intent.ACTION_MAIN)
intent.addCategory(Intent.CATEGORY_HOME)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
System.exit(0)
}.show()
}
}
}.launchIn(fragment.lifecycleScope)
}
}

View File

@@ -1,451 +0,0 @@
/*
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.updater
import android.Manifest
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
import android.os.Build
import android.util.Base64
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.core.content.IntentCompat
import com.wireguard.android.Application
import com.wireguard.android.BuildConfig
import com.wireguard.android.activity.MainActivity
import com.wireguard.android.util.UserKnobs
import com.wireguard.android.util.applicationScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
import java.nio.charset.StandardCharsets
import java.security.InvalidKeyException
import java.security.InvalidParameterException
import java.security.MessageDigest
import java.util.UUID
import kotlin.math.max
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
object Updater {
private const val TAG = "WireGuard/Updater"
private const val UPDATE_URL_FMT = "https://download.wireguard.com/android-client/%s"
private const val APK_NAME_PREFIX = BuildConfig.APPLICATION_ID + "-"
private const val APK_NAME_SUFFIX = ".apk"
private const val LATEST_FILE = "latest.sig"
private const val RELEASE_PUBLIC_KEY_BASE64 = "RWTAzwGRYr3EC9px0Ia3fbttz8WcVN6wrOwWp2delz4el6SI8XmkKSMp"
private val CURRENT_VERSION by lazy { Version(BuildConfig.VERSION_NAME) }
private val updaterScope = CoroutineScope(Job() + Dispatchers.IO)
private fun installer(context: Context): String = try {
val packageName = context.packageName
val pm = context.packageManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
pm.getInstallSourceInfo(packageName).installingPackageName ?: ""
} else {
@Suppress("DEPRECATION")
pm.getInstallerPackageName(packageName) ?: ""
}
} catch (_: Throwable) {
""
}
fun installerIsGooglePlay(context: Context): Boolean = installer(context) == "com.android.vending"
sealed class Progress {
object Complete : Progress()
class Available(val version: String) : Progress() {
fun update() {
applicationScope.launch {
UserKnobs.setUpdaterNewerVersionConsented(version)
}
}
}
object Rechecking : Progress()
class Downloading(val bytesDownloaded: ULong, val bytesTotal: ULong) : Progress()
object Installing : Progress()
class NeedsUserIntervention(val intent: Intent, private val id: Int) : Progress() {
private suspend fun installerActive(): Boolean {
if (mutableState.firstOrNull() != this@NeedsUserIntervention)
return true
try {
if (Application.get().packageManager.packageInstaller.getSessionInfo(id)?.isActive == true)
return true
} catch (_: SecurityException) {
return true
}
return false
}
fun markAsDone() {
applicationScope.launch {
if (installerActive())
return@launch
delay(7.seconds)
if (installerActive())
return@launch
emitProgress(Failure(Exception("Ignored by user")))
}
}
}
class Failure(val error: Throwable) : Progress() {
fun retry() {
updaterScope.launch {
downloadAndUpdateWrapErrors()
}
}
}
class Corrupt(private val betterFile: String?) : Progress() {
val downloadUrl: String
get() = UPDATE_URL_FMT.format(betterFile ?: "")
}
}
private val mutableState = MutableStateFlow<Progress>(Progress.Complete)
val state = mutableState.asStateFlow()
private suspend fun emitProgress(progress: Progress, force: Boolean = false) {
if (force || mutableState.firstOrNull()?.javaClass != progress.javaClass)
mutableState.emit(progress)
}
private class Sha256Digest(hex: String) {
val bytes: ByteArray
init {
if (hex.length != 64)
throw InvalidParameterException("SHA256 hashes must be 32 bytes long")
bytes = hex.chunked(2).map { it.toInt(16).toByte() }.toByteArray()
}
}
@OptIn(ExperimentalUnsignedTypes::class)
private class Version(version: String) : Comparable<Version> {
val parts: ULongArray
init {
val strParts = version.split(".")
if (strParts.isEmpty())
throw InvalidParameterException("Version has no parts")
parts = ULongArray(strParts.size)
for (i in parts.indices) {
parts[i] = strParts[i].toULong()
}
}
override fun toString(): String {
return parts.joinToString(".")
}
override fun compareTo(other: Version): Int {
for (i in 0 until max(parts.size, other.parts.size)) {
val lhsPart = if (i < parts.size) parts[i] else 0UL
val rhsPart = if (i < other.parts.size) other.parts[i] else 0UL
if (lhsPart > rhsPart)
return 1
else if (lhsPart < rhsPart)
return -1
}
return 0
}
}
private class Update(val fileName: String, val version: Version, val hash: Sha256Digest)
private fun versionOfFile(name: String): Version? {
if (!name.startsWith(APK_NAME_PREFIX) || !name.endsWith(APK_NAME_SUFFIX))
return null
return try {
Version(name.substring(APK_NAME_PREFIX.length, name.length - APK_NAME_SUFFIX.length))
} catch (_: Throwable) {
null
}
}
private fun verifySignedFileList(signifyDigest: String): List<Update> {
val updates = ArrayList<Update>(1)
val publicKeyBytes = Base64.decode(RELEASE_PUBLIC_KEY_BASE64, Base64.DEFAULT)
if (publicKeyBytes == null || publicKeyBytes.size != 32 + 10 || publicKeyBytes[0] != 'E'.code.toByte() || publicKeyBytes[1] != 'd'.code.toByte())
throw InvalidKeyException("Invalid public key")
val lines = signifyDigest.split("\n", limit = 3)
if (lines.size != 3)
throw InvalidParameterException("Invalid signature format: too few lines")
if (!lines[0].startsWith("untrusted comment: "))
throw InvalidParameterException("Invalid signature format: missing comment")
val signatureBytes = Base64.decode(lines[1], Base64.DEFAULT)
if (signatureBytes == null || signatureBytes.size != 64 + 10)
throw InvalidParameterException("Invalid signature format: wrong sized or missing signature")
for (i in 0..9) {
if (signatureBytes[i] != publicKeyBytes[i])
throw InvalidParameterException("Invalid signature format: wrong signer")
}
if (!Ed25519.verify(
lines[2].toByteArray(StandardCharsets.UTF_8),
signatureBytes.sliceArray(10 until 10 + 64),
publicKeyBytes.sliceArray(10 until 10 + 32)
)
)
throw SecurityException("Invalid signature")
for (line in lines[2].split("\n").dropLastWhile { it.isEmpty() }) {
val components = line.split(" ", limit = 2)
if (components.size != 2)
throw InvalidParameterException("Invalid file list format: too few components")
/* If version is null, it's not a file we understand, but still a legitimate entry, so don't throw. */
val version = versionOfFile(components[1]) ?: continue
updates.add(Update(components[1], version, Sha256Digest(components[0])))
}
return updates
}
private fun checkForUpdates(): Update? {
val connection = URL(UPDATE_URL_FMT.format(LATEST_FILE)).openConnection() as HttpURLConnection
connection.setRequestProperty("User-Agent", Application.USER_AGENT)
connection.connect()
if (connection.responseCode != HttpURLConnection.HTTP_OK)
throw IOException(connection.responseMessage)
var fileListBytes = ByteArray(1024 * 512 /* 512 KiB */)
connection.inputStream.use {
val len = it.read(fileListBytes)
if (len <= 0)
throw IOException("File list is empty")
fileListBytes = fileListBytes.sliceArray(0 until len)
}
return verifySignedFileList(fileListBytes.decodeToString()).maxByOrNull { it.version }
}
private suspend fun downloadAndUpdate() = withContext(Dispatchers.IO) {
val receiver = InstallReceiver()
val context = Application.get().applicationContext
val pendingIntent = withContext(Dispatchers.Main) {
ContextCompat.registerReceiver(context, receiver, IntentFilter(receiver.sessionId), ContextCompat.RECEIVER_NOT_EXPORTED)
PendingIntent.getBroadcast(
context,
0,
Intent(receiver.sessionId).setPackage(context.packageName),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
}
emitProgress(Progress.Rechecking)
val update = checkForUpdates()
if (update == null || update.version <= CURRENT_VERSION) {
emitProgress(Progress.Complete)
return@withContext
}
emitProgress(Progress.Downloading(0UL, 0UL), true)
val connection = URL(UPDATE_URL_FMT.format(update.fileName)).openConnection() as HttpURLConnection
connection.setRequestProperty("User-Agent", Application.USER_AGENT)
connection.connect()
if (connection.responseCode != HttpURLConnection.HTTP_OK)
throw IOException("Update could not be fetched: ${connection.responseCode}")
var downloadedByteLen: ULong = 0UL
val totalByteLen = (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) connection.contentLengthLong else connection.contentLength).toLong().toULong()
val fileBytes = ByteArray(1024 * 32 /* 32 KiB */)
val digest = MessageDigest.getInstance("SHA-256")
emitProgress(Progress.Downloading(downloadedByteLen, totalByteLen), true)
val installer = context.packageManager.packageInstaller
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
params.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED)
params.setAppPackageName(context.packageName) /* Enforces updates; disallows new apps. */
val session = installer.openSession(installer.createSession(params))
var sessionFailure = true
try {
val installDest = session.openWrite(receiver.sessionId, 0, -1)
installDest.use { dest ->
connection.inputStream.use { src ->
while (true) {
val readLen = src.read(fileBytes)
if (readLen <= 0)
break
digest.update(fileBytes, 0, readLen)
dest.write(fileBytes, 0, readLen)
downloadedByteLen += readLen.toUInt()
emitProgress(Progress.Downloading(downloadedByteLen, totalByteLen), true)
if (downloadedByteLen >= 1024UL * 1024UL * 100UL /* 100 MiB */)
throw IOException("File too large")
}
}
}
emitProgress(Progress.Installing)
if (!digest.digest().contentEquals(update.hash.bytes))
throw SecurityException("Update has invalid hash")
sessionFailure = false
} finally {
if (sessionFailure) {
session.abandon()
session.close()
}
}
session.commit(pendingIntent.intentSender)
session.close()
}
private var updating = false
private suspend fun downloadAndUpdateWrapErrors() {
if (updating)
return
updating = true
try {
downloadAndUpdate()
} catch (e: Throwable) {
Log.e(TAG, "Update failure", e)
emitProgress(Progress.Failure(e))
}
updating = false
}
private class InstallReceiver : BroadcastReceiver() {
val sessionId = UUID.randomUUID().toString()
override fun onReceive(context: Context, intent: Intent) {
if (sessionId != intent.action)
return
when (val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE_INVALID)) {
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
val id = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, 0)
val userIntervention = IntentCompat.getParcelableExtra(intent, Intent.EXTRA_INTENT, Intent::class.java)!!
applicationScope.launch {
emitProgress(Progress.NeedsUserIntervention(userIntervention, id))
}
}
PackageInstaller.STATUS_SUCCESS -> {
applicationScope.launch {
emitProgress(Progress.Complete)
}
context.applicationContext.unregisterReceiver(this)
}
else -> {
val id = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, 0)
try {
context.applicationContext.packageManager.packageInstaller.abandonSession(id)
} catch (_: SecurityException) {
}
val message = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) ?: "Installation error $status"
applicationScope.launch {
val e = Exception(message)
Log.e(TAG, "Update failure", e)
emitProgress(Progress.Failure(e))
}
context.applicationContext.unregisterReceiver(this)
}
}
}
}
fun monitorForUpdates() {
if (BuildConfig.DEBUG)
return
val context = Application.get()
if (installerIsGooglePlay(context))
return
if (!if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
@Suppress("DEPRECATION")
context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_PERMISSIONS)
} else {
context.packageManager.getPackageInfo(context.packageName, PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS.toLong()))
}.requestedPermissions.contains(Manifest.permission.REQUEST_INSTALL_PACKAGES)
) {
if (installer(context).isNotEmpty()) {
updaterScope.launch {
val update = try {
checkForUpdates()
} catch (_: Throwable) {
null
}
emitProgress(Progress.Corrupt(update?.fileName))
}
}
return
}
updaterScope.launch {
if (UserKnobs.updaterNewerVersionSeen.firstOrNull()?.let { Version(it) > CURRENT_VERSION } == true)
return@launch
var waitTime = 15
while (true) {
try {
val update = checkForUpdates() ?: continue
if (update.version > CURRENT_VERSION) {
Log.i(TAG, "Update available: ${update.version}")
UserKnobs.setUpdaterNewerVersionSeen(update.version.toString())
return@launch
}
} catch (_: Throwable) {
}
delay(waitTime.minutes)
waitTime = 45
}
}
UserKnobs.updaterNewerVersionSeen.onEach { ver ->
if (
ver != null &&
Version(ver) > CURRENT_VERSION &&
UserKnobs.updaterNewerVersionConsented.firstOrNull()?.let { Version(it) > CURRENT_VERSION } != true
)
emitProgress(Progress.Available(ver))
}.launchIn(applicationScope)
UserKnobs.updaterNewerVersionConsented.onEach { ver ->
if (ver != null && Version(ver) > CURRENT_VERSION)
updaterScope.launch {
downloadAndUpdateWrapErrors()
}
}.launchIn(applicationScope)
}
class AppUpdatedReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action != Intent.ACTION_MY_PACKAGE_REPLACED)
return
if (installer(context) != context.packageName)
return
/* TODO: does not work because of restrictions placed on broadcast receivers. */
val start = Intent(context, MainActivity::class.java)
start.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
start.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(start)
}
}
}

View File

@@ -1,142 +0,0 @@
/*
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.viewmodel
import android.os.Parcel
import android.os.Parcelable
import androidx.databinding.BaseObservable
import androidx.databinding.Bindable
import androidx.databinding.ObservableArrayList
import androidx.databinding.ObservableList
import com.wireguard.android.BR
import com.wireguard.config.Attribute
import com.wireguard.config.BadConfigException
import com.wireguard.config.Interface
import com.wireguard.crypto.Key
import com.wireguard.crypto.KeyFormatException
import com.wireguard.crypto.KeyPair
class InterfaceProxy : BaseObservable, Parcelable {
@get:Bindable
val excludedApplications: ObservableList<String> = ObservableArrayList()
@get:Bindable
val includedApplications: ObservableList<String> = ObservableArrayList()
@get:Bindable
var addresses: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.addresses)
}
@get:Bindable
var dnsServers: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.dnsServers)
}
@get:Bindable
var listenPort: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.listenPort)
}
@get:Bindable
var mtu: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.mtu)
}
@get:Bindable
var privateKey: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.privateKey)
notifyPropertyChanged(BR.publicKey)
}
@get:Bindable
val publicKey: String
get() = try {
KeyPair(Key.fromBase64(privateKey)).publicKey.toBase64()
} catch (ignored: KeyFormatException) {
""
}
private constructor(parcel: Parcel) {
addresses = parcel.readString() ?: ""
dnsServers = parcel.readString() ?: ""
parcel.readStringList(excludedApplications)
parcel.readStringList(includedApplications)
listenPort = parcel.readString() ?: ""
mtu = parcel.readString() ?: ""
privateKey = parcel.readString() ?: ""
}
constructor(other: Interface) {
addresses = Attribute.join(other.addresses)
val dnsServerStrings = other.dnsServers.map { it.hostAddress }.plus(other.dnsSearchDomains)
dnsServers = Attribute.join(dnsServerStrings)
excludedApplications.addAll(other.excludedApplications)
includedApplications.addAll(other.includedApplications)
listenPort = other.listenPort.map { it.toString() }.orElse("")
mtu = other.mtu.map { it.toString() }.orElse("")
val keyPair = other.keyPair
privateKey = keyPair.privateKey.toBase64()
}
constructor()
override fun describeContents() = 0
fun generateKeyPair() {
val keyPair = KeyPair()
privateKey = keyPair.privateKey.toBase64()
notifyPropertyChanged(BR.privateKey)
notifyPropertyChanged(BR.publicKey)
}
@Throws(BadConfigException::class)
fun resolve(): Interface {
val builder = Interface.Builder()
if (addresses.isNotEmpty()) builder.parseAddresses(addresses)
if (dnsServers.isNotEmpty()) builder.parseDnsServers(dnsServers)
if (excludedApplications.isNotEmpty()) builder.excludeApplications(excludedApplications)
if (includedApplications.isNotEmpty()) builder.includeApplications(includedApplications)
if (listenPort.isNotEmpty()) builder.parseListenPort(listenPort)
if (mtu.isNotEmpty()) builder.parseMtu(mtu)
if (privateKey.isNotEmpty()) builder.parsePrivateKey(privateKey)
return builder.build()
}
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(addresses)
dest.writeString(dnsServers)
dest.writeStringList(excludedApplications)
dest.writeStringList(includedApplications)
dest.writeString(listenPort)
dest.writeString(mtu)
dest.writeString(privateKey)
}
private class InterfaceProxyCreator : Parcelable.Creator<InterfaceProxy> {
override fun createFromParcel(parcel: Parcel): InterfaceProxy {
return InterfaceProxy(parcel)
}
override fun newArray(size: Int): Array<InterfaceProxy?> {
return arrayOfNulls(size)
}
}
companion object {
@JvmField
val CREATOR: Parcelable.Creator<InterfaceProxy> = InterfaceProxyCreator()
}
}

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android
package org.amnezia.awg
import android.content.Context
import android.content.Intent
@@ -17,16 +17,15 @@ import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStoreFile
import com.google.android.material.color.DynamicColors
import com.wireguard.android.backend.Backend
import com.wireguard.android.backend.GoBackend
import com.wireguard.android.backend.WgQuickBackend
import com.wireguard.android.configStore.FileConfigStore
import com.wireguard.android.model.TunnelManager
import com.wireguard.android.updater.Updater
import com.wireguard.android.util.RootShell
import com.wireguard.android.util.ToolsInstaller
import com.wireguard.android.util.UserKnobs
import com.wireguard.android.util.applicationScope
import org.amnezia.awg.backend.Backend
import org.amnezia.awg.backend.GoBackend
import org.amnezia.awg.backend.AwgQuickBackend
import org.amnezia.awg.configStore.FileConfigStore
import org.amnezia.awg.model.TunnelManager
import org.amnezia.awg.util.RootShell
import org.amnezia.awg.util.ToolsInstaller
import org.amnezia.awg.util.UserKnobs
import org.amnezia.awg.util.applicationScope
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -63,14 +62,14 @@ class Application : android.app.Application() {
private suspend fun determineBackend(): Backend {
var backend: Backend? = null
if (UserKnobs.enableKernelModule.first() && WgQuickBackend.hasKernelSupport()) {
if (UserKnobs.enableKernelModule.first() && AwgQuickBackend.hasKernelSupport()) {
try {
rootShell.start()
val wgQuickBackend = WgQuickBackend(applicationContext, rootShell, toolsInstaller)
wgQuickBackend.setMultipleTunnels(UserKnobs.multipleTunnels.first())
backend = wgQuickBackend
val awgQuickBackend = AwgQuickBackend(applicationContext, rootShell, toolsInstaller)
awgQuickBackend.setMultipleTunnels(UserKnobs.multipleTunnels.first())
backend = awgQuickBackend
UserKnobs.multipleTunnels.onEach {
wgQuickBackend.setMultipleTunnels(it)
awgQuickBackend.setMultipleTunnels(it)
}.launchIn(coroutineScope)
} catch (ignored: Exception) {
}
@@ -116,7 +115,6 @@ class Application : android.app.Application() {
Log.e(TAG, Log.getStackTraceString(e))
}
}
Updater.monitorForUpdates()
if (BuildConfig.DEBUG) {
StrictMode.setVmPolicy(VmPolicy.Builder().detectAll().penaltyLog().build())
@@ -130,8 +128,8 @@ class Application : android.app.Application() {
}
companion object {
val USER_AGENT = String.format(Locale.ENGLISH, "WireGuard/%s (Android %d; %s; %s; %s %s; %s)", BuildConfig.VERSION_NAME, Build.VERSION.SDK_INT, if (Build.SUPPORTED_ABIS.isNotEmpty()) Build.SUPPORTED_ABIS[0] else "unknown ABI", Build.BOARD, Build.MANUFACTURER, Build.MODEL, Build.FINGERPRINT)
private const val TAG = "WireGuard/Application"
val USER_AGENT = String.format(Locale.ENGLISH, "AmneziaWG/%s (Android %d; %s; %s; %s %s; %s)", BuildConfig.VERSION_NAME, Build.VERSION.SDK_INT, if (Build.SUPPORTED_ABIS.isNotEmpty()) Build.SUPPORTED_ABIS[0] else "unknown ABI", Build.BOARD, Build.MANUFACTURER, Build.MODEL, Build.FINGERPRINT)
private const val TAG = "AmneziaWG/Application"
private lateinit var weakSelf: WeakReference<Application>
fun get(): Application {

View File

@@ -2,21 +2,21 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android
package org.amnezia.awg
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.wireguard.android.backend.WgQuickBackend
import com.wireguard.android.util.applicationScope
import org.amnezia.awg.backend.AwgQuickBackend
import org.amnezia.awg.util.applicationScope
import kotlinx.coroutines.launch
class BootShutdownReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action ?: return
applicationScope.launch {
if (Application.getBackend() !is WgQuickBackend) return@launch
if (Application.getBackend() !is AwgQuickBackend) return@launch
val tunnelManager = Application.getTunnelManager()
if (Intent.ACTION_BOOT_COMPLETED == action) {
Log.i(TAG, "Broadcast receiver restoring state (boot)")
@@ -29,6 +29,6 @@ class BootShutdownReceiver : BroadcastReceiver() {
}
companion object {
private const val TAG = "WireGuard/BootShutdownReceiver"
private const val TAG = "AmneziaWG/BootShutdownReceiver"
}
}

View File

@@ -2,27 +2,29 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android
package org.amnezia.awg
import android.app.PendingIntent
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.Icon
import android.net.Uri
import android.os.Build
import android.os.IBinder
import android.provider.Settings
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.databinding.Observable
import androidx.databinding.Observable.OnPropertyChangedCallback
import com.wireguard.android.activity.MainActivity
import com.wireguard.android.activity.TunnelToggleActivity
import com.wireguard.android.backend.Tunnel
import com.wireguard.android.model.ObservableTunnel
import com.wireguard.android.util.applicationScope
import com.wireguard.android.widget.SlashDrawable
import org.amnezia.awg.activity.MainActivity
import org.amnezia.awg.activity.TunnelToggleActivity
import org.amnezia.awg.backend.Tunnel
import org.amnezia.awg.model.ObservableTunnel
import org.amnezia.awg.util.applicationScope
import org.amnezia.awg.widget.SlashDrawable
import kotlinx.coroutines.launch
/**
@@ -55,7 +57,7 @@ class QuickTileService : TileService() {
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
startActivityAndCollapse(PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE))
startActivityAndCollapse(PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE))
} else {
@Suppress("DEPRECATION")
startActivityAndCollapse(intent)
@@ -68,6 +70,12 @@ class QuickTileService : TileService() {
tunnel.setStateAsync(Tunnel.State.TOGGLE)
updateTile()
} catch (_: Throwable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && !Settings.canDrawOverlays(this@QuickTileService)) {
val permissionIntent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName"))
permissionIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivityAndCollapse(PendingIntent.getActivity(this@QuickTileService, 0, permissionIntent, PendingIntent.FLAG_IMMUTABLE))
return@launch
}
val toggleIntent = Intent(this@QuickTileService, TunnelToggleActivity::class.java)
toggleIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(toggleIntent)
@@ -172,7 +180,7 @@ class QuickTileService : TileService() {
}
companion object {
private const val TAG = "WireGuard/QuickTileService"
private const val TAG = "AmneziaWG/QuickTileService"
var isAdded: Boolean = false
private set
}

View File

@@ -2,15 +2,15 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.activity
package org.amnezia.awg.activity
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.CallbackRegistry
import androidx.databinding.CallbackRegistry.NotifierCallback
import androidx.lifecycle.lifecycleScope
import com.wireguard.android.Application
import com.wireguard.android.model.ObservableTunnel
import org.amnezia.awg.Application
import org.amnezia.awg.model.ObservableTunnel
import kotlinx.coroutines.launch
/**

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.activity
package org.amnezia.awg.activity
import android.content.ClipDescription.compareMimeTypes
import android.content.ContentProvider
@@ -36,13 +36,13 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textview.MaterialTextView
import com.wireguard.android.BuildConfig
import com.wireguard.android.R
import com.wireguard.android.databinding.LogViewerActivityBinding
import com.wireguard.android.util.DownloadsFileSaver
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.resolveAttribute
import com.wireguard.crypto.KeyPair
import org.amnezia.awg.BuildConfig
import org.amnezia.awg.R
import org.amnezia.awg.databinding.LogViewerActivityBinding
import org.amnezia.awg.util.DownloadsFileSaver
import org.amnezia.awg.util.ErrorMessages
import org.amnezia.awg.util.resolveAttribute
import org.amnezia.awg.crypto.KeyPair
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -171,7 +171,7 @@ class LogViewerActivity : AppCompatActivity() {
var outputFile: DownloadsFileSaver.DownloadsFile? = null
withContext(Dispatchers.IO) {
try {
outputFile = downloadsFileSaver.save("wireguard-log.txt", "text/plain", true)
outputFile = downloadsFileSaver.save("amneziawg-log.txt", "text/plain", true)
outputFile?.outputStream?.write(rawLogBytes())
} catch (e: Throwable) {
outputFile?.delete()
@@ -223,7 +223,7 @@ class LogViewerActivity : AppCompatActivity() {
} else {
if (bufferedLogLines.isNotEmpty()) {
bufferedLogLines.last().msg += "\n$line"
} else if (!logLines.isEmpty) {
} else if (!logLines.isEmpty()) {
logLines[logLines.size() - 1].msg += "\n$line"
priorModified = true
}
@@ -294,7 +294,7 @@ class LogViewerActivity : AppCompatActivity() {
private val THREADTIME_LINE: Pattern =
Pattern.compile("^(\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}.\\d{3})(?:\\s+[0-9A-Za-z]+)?\\s+(\\d+)\\s+(\\d+)\\s+([A-Z])\\s+(.+?)\\s*: (.*)$")
private val LOGS: MutableMap<String, ByteArray> = ConcurrentHashMap()
private const val TAG = "WireGuard/LogViewerActivity"
private const val TAG = "AmneziaWG/LogViewerActivity"
}
private inner class LogEntryAdapter : RecyclerView.Adapter<LogEntryAdapter.ViewHolder>() {
@@ -353,7 +353,7 @@ class LogViewerActivity : AppCompatActivity() {
override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? =
logForUri(uri)?.let {
val m = MatrixCursor(arrayOf(android.provider.OpenableColumns.DISPLAY_NAME, android.provider.OpenableColumns.SIZE), 1)
m.addRow(arrayOf("wireguard-log.txt", it.size.toLong()))
m.addRow(arrayOf("amneziawg-log.txt", it.size.toLong()))
m
}

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.activity
package org.amnezia.awg.activity
import android.content.Intent
import android.os.Bundle
@@ -15,15 +15,15 @@ import androidx.appcompat.app.ActionBar
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import androidx.fragment.app.commit
import com.wireguard.android.R
import com.wireguard.android.fragment.TunnelDetailFragment
import com.wireguard.android.fragment.TunnelEditorFragment
import com.wireguard.android.model.ObservableTunnel
import org.amnezia.awg.R
import org.amnezia.awg.fragment.TunnelDetailFragment
import org.amnezia.awg.fragment.TunnelEditorFragment
import org.amnezia.awg.model.ObservableTunnel
/**
* CRUD interface for WireGuard tunnels. This activity serves as the main entry point to the
* WireGuard application, and contains several fragments for listing, viewing details of, and
* editing the configuration and interface state of WireGuard tunnels.
* CRUD interface for AmneziaWG tunnels. This activity serves as the main entry point to the
* AmneziaWG application, and contains several fragments for listing, viewing details of, and
* editing the configuration and interface state of AmneziaWG tunnels.
*/
class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener {
private var actionBar: ActionBar? = null

View File

@@ -2,25 +2,23 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.activity
package org.amnezia.awg.activity
import android.content.ComponentName
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.service.quicksettings.TileService
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.commit
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.wireguard.android.Application
import com.wireguard.android.QuickTileService
import com.wireguard.android.R
import com.wireguard.android.backend.WgQuickBackend
import com.wireguard.android.preference.PreferencesPreferenceDataStore
import com.wireguard.android.util.AdminKnobs
import org.amnezia.awg.Application
import org.amnezia.awg.QuickTileService
import org.amnezia.awg.R
import org.amnezia.awg.backend.AwgQuickBackend
import org.amnezia.awg.preference.PreferencesPreferenceDataStore
import org.amnezia.awg.util.AdminKnobs
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -62,26 +60,22 @@ class SettingsActivity : AppCompatActivity() {
darkTheme?.parent?.removePreference(darkTheme)
--preferenceScreen.initialExpandedChildrenCount
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
val remoteApps = preferenceManager.findPreference<Preference>("allow_remote_control_intents")
remoteApps?.parent?.removePreference(remoteApps)
}
if (AdminKnobs.disableConfigExport) {
val zipExporter = preferenceManager.findPreference<Preference>("zip_exporter")
zipExporter?.parent?.removePreference(zipExporter)
}
val wgQuickOnlyPrefs = arrayOf(
val awgQuickOnlyPrefs = arrayOf(
preferenceManager.findPreference("tools_installer"),
preferenceManager.findPreference("restore_on_boot"),
preferenceManager.findPreference<Preference>("multiple_tunnels")
).filterNotNull()
wgQuickOnlyPrefs.forEach { it.isVisible = false }
awgQuickOnlyPrefs.forEach { it.isVisible = false }
lifecycleScope.launch {
if (Application.getBackend() is WgQuickBackend) {
if (Application.getBackend() is AwgQuickBackend) {
++preferenceScreen.initialExpandedChildrenCount
wgQuickOnlyPrefs.forEach { it.isVisible = true }
awgQuickOnlyPrefs.forEach { it.isVisible = true }
} else {
wgQuickOnlyPrefs.forEach { it.parent?.removePreference(it) }
awgQuickOnlyPrefs.forEach { it.parent?.removePreference(it) }
}
}
preferenceManager.findPreference<Preference>("log_viewer")?.setOnPreferenceClickListener {
@@ -89,9 +83,9 @@ class SettingsActivity : AppCompatActivity() {
true
}
val kernelModuleEnabler = preferenceManager.findPreference<Preference>("kernel_module_enabler")
if (WgQuickBackend.hasKernelSupport()) {
if (AwgQuickBackend.hasKernelSupport()) {
lifecycleScope.launch {
if (Application.getBackend() !is WgQuickBackend) {
if (Application.getBackend() !is AwgQuickBackend) {
try {
withContext(Dispatchers.IO) { Application.getRootShell().start() }
} catch (_: Throwable) {

View File

@@ -2,12 +2,12 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.activity
package org.amnezia.awg.activity
import android.os.Bundle
import androidx.fragment.app.commit
import com.wireguard.android.fragment.TunnelEditorFragment
import com.wireguard.android.model.ObservableTunnel
import org.amnezia.awg.fragment.TunnelEditorFragment
import org.amnezia.awg.model.ObservableTunnel
/**
* Standalone activity for creating tunnels.

View File

@@ -2,27 +2,24 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.activity
package org.amnezia.awg.activity
import android.content.ComponentName
import android.os.Build
import android.os.Bundle
import android.service.quicksettings.TileService
import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.wireguard.android.Application
import com.wireguard.android.QuickTileService
import com.wireguard.android.R
import com.wireguard.android.backend.GoBackend
import com.wireguard.android.backend.Tunnel
import com.wireguard.android.util.ErrorMessages
import org.amnezia.awg.Application
import org.amnezia.awg.QuickTileService
import org.amnezia.awg.R
import org.amnezia.awg.backend.GoBackend
import org.amnezia.awg.backend.Tunnel
import org.amnezia.awg.util.ErrorMessages
import kotlinx.coroutines.launch
@RequiresApi(Build.VERSION_CODES.N)
class TunnelToggleActivity : AppCompatActivity() {
private val permissionActivityResultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { toggleTunnelWithPermissionsResult() }
@@ -61,6 +58,6 @@ class TunnelToggleActivity : AppCompatActivity() {
}
companion object {
private const val TAG = "WireGuard/TunnelToggleActivity"
private const val TAG = "AmneziaWG/TunnelToggleActivity"
}
}

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.activity
package org.amnezia.awg.activity
import android.Manifest
import android.content.ActivityNotFoundException
@@ -34,22 +34,22 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.backend.GoBackend
import com.wireguard.android.backend.Tunnel
import com.wireguard.android.databinding.Keyed
import com.wireguard.android.databinding.ObservableKeyedArrayList
import com.wireguard.android.databinding.ObservableKeyedRecyclerViewAdapter
import com.wireguard.android.databinding.TvActivityBinding
import com.wireguard.android.databinding.TvFileListItemBinding
import com.wireguard.android.databinding.TvTunnelListItemBinding
import com.wireguard.android.model.ObservableTunnel
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.QuantityFormatter
import com.wireguard.android.util.TunnelImporter
import com.wireguard.android.util.UserKnobs
import com.wireguard.android.util.applicationScope
import org.amnezia.awg.Application
import org.amnezia.awg.R
import org.amnezia.awg.backend.GoBackend
import org.amnezia.awg.backend.Tunnel
import org.amnezia.awg.databinding.Keyed
import org.amnezia.awg.databinding.ObservableKeyedArrayList
import org.amnezia.awg.databinding.ObservableKeyedRecyclerViewAdapter
import org.amnezia.awg.databinding.TvActivityBinding
import org.amnezia.awg.databinding.TvFileListItemBinding
import org.amnezia.awg.databinding.TvTunnelListItemBinding
import org.amnezia.awg.model.ObservableTunnel
import org.amnezia.awg.util.ErrorMessages
import org.amnezia.awg.util.QuantityFormatter
import org.amnezia.awg.util.TunnelImporter
import org.amnezia.awg.util.UserKnobs
import org.amnezia.awg.util.applicationScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -263,31 +263,14 @@ class TvMainActivity : AppCompatActivity() {
private suspend fun makeStorageRoots(): Collection<KeyedFile> = withContext(Dispatchers.IO) {
cachedRoots?.let { return@withContext it }
val list = HashSet<KeyedFile>()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val storageManager: StorageManager = getSystemService() ?: return@withContext list
list.addAll(storageManager.storageVolumes.mapNotNull { volume ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
volume.directory?.let { KeyedFile(it, volume.getDescription(this@TvMainActivity)) }
} else {
KeyedFile((StorageVolume::class.java.getMethod("getPathFile").invoke(volume) as File), volume.getDescription(this@TvMainActivity))
}
})
} else {
@Suppress("DEPRECATION")
list.add(KeyedFile(Environment.getExternalStorageDirectory()))
try {
File("/storage").listFiles()?.forEach {
if (!it.isDirectory) return@forEach
try {
if (Environment.isExternalStorageRemovable(it)) {
list.add(KeyedFile(it))
}
} catch (_: Throwable) {
}
}
} catch (_: Throwable) {
val storageManager: StorageManager = getSystemService() ?: return@withContext list
list.addAll(storageManager.storageVolumes.mapNotNull { volume ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
volume.directory?.let { KeyedFile(it, volume.getDescription(this@TvMainActivity)) }
} else {
KeyedFile((StorageVolume::class.java.getMethod("getPathFile").invoke(volume) as File), volume.getDescription(this@TvMainActivity))
}
}
})
cachedRoots = list
list
}
@@ -440,6 +423,6 @@ class TvMainActivity : AppCompatActivity() {
}
companion object {
private const val TAG = "WireGuard/TvMainActivity"
private const val TAG = "AmneziaWG/TvMainActivity"
}
}

View File

@@ -2,12 +2,12 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.configStore
package org.amnezia.awg.configStore
import com.wireguard.config.Config
import org.amnezia.awg.config.Config
/**
* Interface for persistent storage providers for WireGuard configurations.
* Interface for persistent storage providers for AmneziaWG configurations.
*/
interface ConfigStore {
/**

View File

@@ -2,13 +2,13 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.configStore
package org.amnezia.awg.configStore
import android.content.Context
import android.util.Log
import com.wireguard.android.R
import com.wireguard.config.BadConfigException
import com.wireguard.config.Config
import org.amnezia.awg.R
import org.amnezia.awg.config.BadConfigException
import org.amnezia.awg.config.Config
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
@@ -17,7 +17,7 @@ import java.io.IOException
import java.nio.charset.StandardCharsets
/**
* Configuration store that uses a `wg-quick`-style file for each configured tunnel.
* Configuration store that uses a `awg-quick`-style file for each configured tunnel.
*/
class FileConfigStore(private val context: Context) : ConfigStore {
@Throws(IOException::class)
@@ -26,7 +26,7 @@ class FileConfigStore(private val context: Context) : ConfigStore {
val file = fileFor(name)
if (!file.createNewFile())
throw IOException(context.getString(R.string.config_file_exists_error, file.name))
FileOutputStream(file, false).use { it.write(config.toWgQuickString().toByteArray(StandardCharsets.UTF_8)) }
FileOutputStream(file, false).use { it.write(config.toAwgQuickString().toByteArray(StandardCharsets.UTF_8)) }
return config
}
@@ -72,11 +72,11 @@ class FileConfigStore(private val context: Context) : ConfigStore {
val file = fileFor(name)
if (!file.isFile)
throw FileNotFoundException(context.getString(R.string.config_not_found_error, file.name))
FileOutputStream(file, false).use { stream -> stream.write(config.toWgQuickString().toByteArray(StandardCharsets.UTF_8)) }
FileOutputStream(file, false).use { stream -> stream.write(config.toAwgQuickString().toByteArray(StandardCharsets.UTF_8)) }
return config
}
companion object {
private const val TAG = "WireGuard/FileConfigStore"
private const val TAG = "AmneziaWG/FileConfigStore"
}
}

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.databinding
package org.amnezia.awg.databinding
import android.text.InputFilter
import android.view.LayoutInflater
@@ -18,14 +18,14 @@ import androidx.databinding.adapters.ListenerUtil
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.wireguard.android.BR
import com.wireguard.android.R
import com.wireguard.android.databinding.ObservableKeyedRecyclerViewAdapter.RowConfigurationHandler
import com.wireguard.android.widget.ToggleSwitch
import com.wireguard.android.widget.ToggleSwitch.OnBeforeCheckedChangeListener
import com.wireguard.android.widget.TvCardView
import com.wireguard.config.Attribute
import com.wireguard.config.InetNetwork
import org.amnezia.awg.BR
import org.amnezia.awg.R
import org.amnezia.awg.databinding.ObservableKeyedRecyclerViewAdapter.RowConfigurationHandler
import org.amnezia.awg.widget.ToggleSwitch
import org.amnezia.awg.widget.ToggleSwitch.OnBeforeCheckedChangeListener
import org.amnezia.awg.widget.TvCardView
import org.amnezia.awg.config.Attribute
import org.amnezia.awg.config.InetNetwork
import java.net.InetAddress
import java.util.Optional

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.databinding
package org.amnezia.awg.databinding
import android.view.LayoutInflater
import android.view.View
@@ -11,7 +11,7 @@ import androidx.databinding.DataBindingUtil
import androidx.databinding.ObservableList
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import com.wireguard.android.BR
import org.amnezia.awg.BR
import java.lang.ref.WeakReference
/**

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.databinding
package org.amnezia.awg.databinding
/**
* Interface for objects that have a identifying key of the given type.

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.databinding
package org.amnezia.awg.databinding
import androidx.databinding.ObservableArrayList

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.databinding
package org.amnezia.awg.databinding
import android.content.Context
import android.view.LayoutInflater
@@ -11,7 +11,7 @@ import androidx.databinding.DataBindingUtil
import androidx.databinding.ObservableList
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView
import com.wireguard.android.BR
import org.amnezia.awg.BR
import java.lang.ref.WeakReference
/**

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.databinding
package org.amnezia.awg.databinding
import java.util.AbstractList
import java.util.Collections

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.fragment
package org.amnezia.awg.fragment
import android.content.pm.PackageManager
import android.graphics.drawable.GradientDrawable
@@ -17,8 +17,8 @@ import androidx.fragment.app.setFragmentResult
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.wireguard.android.R
import com.wireguard.android.util.resolveAttribute
import org.amnezia.awg.R
import org.amnezia.awg.util.resolveAttribute
class AddTunnelsSheet : BottomSheetDialogFragment() {

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.fragment
package org.amnezia.awg.fragment
import android.Manifest
import android.app.Dialog
@@ -21,12 +21,12 @@ import androidx.fragment.app.setFragmentResult
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayout
import com.wireguard.android.BR
import com.wireguard.android.R
import com.wireguard.android.databinding.AppListDialogFragmentBinding
import com.wireguard.android.databinding.ObservableKeyedArrayList
import com.wireguard.android.model.ApplicationData
import com.wireguard.android.util.ErrorMessages
import org.amnezia.awg.BR
import org.amnezia.awg.R
import org.amnezia.awg.databinding.AppListDialogFragmentBinding
import org.amnezia.awg.databinding.ObservableKeyedArrayList
import org.amnezia.awg.model.ApplicationData
import org.amnezia.awg.util.ErrorMessages
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.fragment
package org.amnezia.awg.fragment
import android.content.Context
import android.util.Log
@@ -14,16 +14,16 @@ import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.activity.BaseActivity
import com.wireguard.android.activity.BaseActivity.OnSelectedTunnelChangedListener
import com.wireguard.android.backend.GoBackend
import com.wireguard.android.backend.Tunnel
import com.wireguard.android.databinding.TunnelDetailFragmentBinding
import com.wireguard.android.databinding.TunnelListItemBinding
import com.wireguard.android.model.ObservableTunnel
import com.wireguard.android.util.ErrorMessages
import org.amnezia.awg.Application
import org.amnezia.awg.R
import org.amnezia.awg.activity.BaseActivity
import org.amnezia.awg.activity.BaseActivity.OnSelectedTunnelChangedListener
import org.amnezia.awg.backend.GoBackend
import org.amnezia.awg.backend.Tunnel
import org.amnezia.awg.databinding.TunnelDetailFragmentBinding
import org.amnezia.awg.databinding.TunnelListItemBinding
import org.amnezia.awg.model.ObservableTunnel
import org.amnezia.awg.util.ErrorMessages
import kotlinx.coroutines.launch
/**
@@ -109,6 +109,6 @@ abstract class BaseFragment : Fragment(), OnSelectedTunnelChangedListener {
}
companion object {
private const val TAG = "WireGuard/BaseFragment"
private const val TAG = "AmneziaWG/BaseFragment"
}
}

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.fragment
package org.amnezia.awg.fragment
import android.app.Dialog
import android.os.Bundle
@@ -10,11 +10,11 @@ import android.view.WindowManager
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
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 org.amnezia.awg.Application
import org.amnezia.awg.R
import org.amnezia.awg.databinding.ConfigNamingDialogFragmentBinding
import org.amnezia.awg.config.BadConfigException
import org.amnezia.awg.config.Config
import kotlinx.coroutines.launch
import java.io.ByteArrayInputStream
import java.io.IOException

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.fragment
package org.amnezia.awg.fragment
import android.os.Bundle
import android.view.LayoutInflater
@@ -15,12 +15,12 @@ import androidx.core.view.MenuProvider
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import com.wireguard.android.R
import com.wireguard.android.backend.Tunnel
import com.wireguard.android.databinding.TunnelDetailFragmentBinding
import com.wireguard.android.databinding.TunnelDetailPeerBinding
import com.wireguard.android.model.ObservableTunnel
import com.wireguard.android.util.QuantityFormatter
import org.amnezia.awg.R
import org.amnezia.awg.backend.Tunnel
import org.amnezia.awg.databinding.TunnelDetailFragmentBinding
import org.amnezia.awg.databinding.TunnelDetailPeerBinding
import org.amnezia.awg.model.ObservableTunnel
import org.amnezia.awg.util.QuantityFormatter
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.fragment
package org.amnezia.awg.fragment
import android.content.Context
import android.os.Bundle
@@ -23,20 +23,20 @@ import androidx.core.view.MenuProvider
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.backend.Tunnel
import com.wireguard.android.databinding.TunnelEditorFragmentBinding
import com.wireguard.android.model.ObservableTunnel
import com.wireguard.android.util.AdminKnobs
import com.wireguard.android.util.BiometricAuthenticator
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.viewmodel.ConfigProxy
import com.wireguard.config.Config
import org.amnezia.awg.Application
import org.amnezia.awg.R
import org.amnezia.awg.backend.Tunnel
import org.amnezia.awg.databinding.TunnelEditorFragmentBinding
import org.amnezia.awg.model.ObservableTunnel
import org.amnezia.awg.util.AdminKnobs
import org.amnezia.awg.util.BiometricAuthenticator
import org.amnezia.awg.util.ErrorMessages
import org.amnezia.awg.viewmodel.ConfigProxy
import org.amnezia.awg.config.Config
import kotlinx.coroutines.launch
/**
* Fragment for editing a WireGuard configuration.
* Fragment for editing an AmneziaWG configuration.
*/
class TunnelEditorFragment : BaseFragment(), MenuProvider {
private var haveShownKeys = false
@@ -328,6 +328,6 @@ class TunnelEditorFragment : BaseFragment(), MenuProvider {
companion object {
private const val KEY_LOCAL_CONFIG = "local_config"
private const val KEY_ORIGINAL_NAME = "original_name"
private const val TAG = "WireGuard/TunnelEditorFragment"
private const val TAG = "AmneziaWG/TunnelEditorFragment"
}
}

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.fragment
package org.amnezia.awg.fragment
import android.content.Intent
import android.content.res.Resources
@@ -26,25 +26,24 @@ import com.google.android.material.snackbar.Snackbar
import com.google.zxing.qrcode.QRCodeReader
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanOptions
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.activity.TunnelCreatorActivity
import com.wireguard.android.databinding.ObservableKeyedRecyclerViewAdapter.RowConfigurationHandler
import com.wireguard.android.databinding.TunnelListFragmentBinding
import com.wireguard.android.databinding.TunnelListItemBinding
import com.wireguard.android.model.ObservableTunnel
import com.wireguard.android.updater.SnackbarUpdateShower
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.QrCodeFromFileScanner
import com.wireguard.android.util.TunnelImporter
import com.wireguard.android.widget.MultiselectableRelativeLayout
import org.amnezia.awg.Application
import org.amnezia.awg.R
import org.amnezia.awg.activity.TunnelCreatorActivity
import org.amnezia.awg.databinding.ObservableKeyedRecyclerViewAdapter.RowConfigurationHandler
import org.amnezia.awg.databinding.TunnelListFragmentBinding
import org.amnezia.awg.databinding.TunnelListItemBinding
import org.amnezia.awg.model.ObservableTunnel
import org.amnezia.awg.util.ErrorMessages
import org.amnezia.awg.util.QrCodeFromFileScanner
import org.amnezia.awg.util.TunnelImporter
import org.amnezia.awg.widget.MultiselectableRelativeLayout
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
/**
* Fragment containing a list of known WireGuard tunnels. It allows creating and deleting tunnels.
* Fragment containing a list of known AmneziaWG tunnels. It allows creating and deleting tunnels.
*/
class TunnelListFragment : BaseFragment() {
private val actionModeListener = ActionModeListener()
@@ -81,8 +80,6 @@ class TunnelListFragment : BaseFragment() {
}
}
private val snackbarUpdateShower = SnackbarUpdateShower(this)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (savedInstanceState != null) {
@@ -127,7 +124,6 @@ class TunnelListFragment : BaseFragment() {
bottomSheet.showNow(childFragmentManager, "BOTTOM_SHEET")
}
executePendingBindings()
snackbarUpdateShower.attach(mainContainer, createFab)
}
backPressedCallback = requireActivity().onBackPressedDispatcher.addCallback(this) { actionMode?.finish() }
backPressedCallback?.isEnabled = false
@@ -337,6 +333,6 @@ class TunnelListFragment : BaseFragment() {
companion object {
private const val CHECKED_ITEMS = "CHECKED_ITEMS"
private const val TAG = "WireGuard/TunnelListFragment"
private const val TAG = "AmneziaWG/TunnelListFragment"
}
}

View File

@@ -2,13 +2,13 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.model
package org.amnezia.awg.model
import android.graphics.drawable.Drawable
import androidx.databinding.BaseObservable
import androidx.databinding.Bindable
import com.wireguard.android.BR
import com.wireguard.android.databinding.Keyed
import org.amnezia.awg.BR
import org.amnezia.awg.databinding.Keyed
class ApplicationData(val icon: Drawable, val name: String, val packageName: String, isSelected: Boolean) : BaseObservable(), Keyed<String> {
override val key = name

View File

@@ -2,23 +2,23 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.model
package org.amnezia.awg.model
import android.util.Log
import androidx.databinding.BaseObservable
import androidx.databinding.Bindable
import com.wireguard.android.BR
import com.wireguard.android.backend.Statistics
import com.wireguard.android.backend.Tunnel
import com.wireguard.android.databinding.Keyed
import com.wireguard.android.util.applicationScope
import com.wireguard.config.Config
import org.amnezia.awg.BR
import org.amnezia.awg.backend.Statistics
import org.amnezia.awg.backend.Tunnel
import org.amnezia.awg.databinding.Keyed
import org.amnezia.awg.util.applicationScope
import org.amnezia.awg.config.Config
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**
* Encapsulates the volatile and nonvolatile state of a WireGuard tunnel.
* Encapsulates the volatile and nonvolatile state of an AmneziaWG tunnel.
*/
class ObservableTunnel internal constructor(
private val manager: TunnelManager,
@@ -141,6 +141,6 @@ class ObservableTunnel internal constructor(
companion object {
private const val TAG = "WireGuard/ObservableTunnel"
private const val TAG = "AmneziaWG/ObservableTunnel"
}
}

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.model
package org.amnezia.awg.model
object TunnelComparator : Comparator<String> {
private class NaturalSortString(originalString: String) {

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.model
package org.amnezia.awg.model
import android.content.BroadcastReceiver
import android.content.Context
@@ -12,19 +12,19 @@ import android.util.Log
import android.widget.Toast
import androidx.databinding.BaseObservable
import androidx.databinding.Bindable
import com.wireguard.android.Application.Companion.get
import com.wireguard.android.Application.Companion.getBackend
import com.wireguard.android.Application.Companion.getTunnelManager
import com.wireguard.android.BR
import com.wireguard.android.R
import com.wireguard.android.backend.Statistics
import com.wireguard.android.backend.Tunnel
import com.wireguard.android.configStore.ConfigStore
import com.wireguard.android.databinding.ObservableSortedKeyedArrayList
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.UserKnobs
import com.wireguard.android.util.applicationScope
import com.wireguard.config.Config
import org.amnezia.awg.Application.Companion.get
import org.amnezia.awg.Application.Companion.getBackend
import org.amnezia.awg.Application.Companion.getTunnelManager
import org.amnezia.awg.BR
import org.amnezia.awg.R
import org.amnezia.awg.backend.Statistics
import org.amnezia.awg.backend.Tunnel
import org.amnezia.awg.configStore.ConfigStore
import org.amnezia.awg.databinding.ObservableSortedKeyedArrayList
import org.amnezia.awg.util.ErrorMessages
import org.amnezia.awg.util.UserKnobs
import org.amnezia.awg.util.applicationScope
import org.amnezia.awg.config.Config
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@@ -35,7 +35,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**
* Maintains and mediates changes to the set of available WireGuard tunnels,
* Maintains and mediates changes to the set of available AmneziaWG tunnels,
*/
class TunnelManager(private val configStore: ConfigStore) : BaseObservable() {
private val tunnels = CompletableDeferred<ObservableSortedKeyedArrayList<String, ObservableTunnel>>()
@@ -217,7 +217,7 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() {
val manager = getTunnelManager()
if (intent == null) return@launch
val action = intent.action ?: return@launch
if ("com.wireguard.android.action.REFRESH_TUNNEL_STATES" == action) {
if ("org.amnezia.awg.action.REFRESH_TUNNEL_STATES" == action) {
manager.refreshTunnelStates()
return@launch
}
@@ -225,8 +225,8 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() {
return@launch
val state: Tunnel.State
state = when (action) {
"com.wireguard.android.action.SET_TUNNEL_UP" -> Tunnel.State.UP
"com.wireguard.android.action.SET_TUNNEL_DOWN" -> Tunnel.State.DOWN
"org.amnezia.awg.action.SET_TUNNEL_UP" -> Tunnel.State.UP
"org.amnezia.awg.action.SET_TUNNEL_DOWN" -> Tunnel.State.DOWN
else -> return@launch
}
val tunnelName = intent.getStringExtra("tunnel") ?: return@launch
@@ -250,6 +250,6 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() {
}
companion object {
private const val TAG = "WireGuard/TunnelManager"
private const val TAG = "AmneziaWG/TunnelManager"
}
}

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.preference
package org.amnezia.awg.preference
import android.content.Context
import android.content.Intent
@@ -10,14 +10,14 @@ import android.util.AttributeSet
import android.util.Log
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.activity.SettingsActivity
import com.wireguard.android.backend.Tunnel
import com.wireguard.android.backend.WgQuickBackend
import com.wireguard.android.util.UserKnobs
import com.wireguard.android.util.activity
import com.wireguard.android.util.lifecycleScope
import org.amnezia.awg.Application
import org.amnezia.awg.R
import org.amnezia.awg.activity.SettingsActivity
import org.amnezia.awg.backend.Tunnel
import org.amnezia.awg.backend.AwgQuickBackend
import org.amnezia.awg.util.UserKnobs
import org.amnezia.awg.util.activity
import org.amnezia.awg.util.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
@@ -32,7 +32,7 @@ class KernelModuleEnablerPreference(context: Context, attrs: AttributeSet?) : Pr
init {
isVisible = false
lifecycleScope.launch {
setState(if (Application.getBackend() is WgQuickBackend) State.ENABLED else State.DISABLED)
setState(if (Application.getBackend() is AwgQuickBackend) State.ENABLED else State.DISABLED)
}
}
@@ -83,6 +83,6 @@ class KernelModuleEnablerPreference(context: Context, attrs: AttributeSet?) : Pr
}
companion object {
private const val TAG = "WireGuard/KernelModuleEnablerPreference"
private const val TAG = "AmneziaWG/KernelModuleEnablerPreference"
}
}

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.preference
package org.amnezia.awg.preference
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.preference
package org.amnezia.awg.preference
import android.app.StatusBarManager
import android.content.ComponentName
@@ -14,8 +14,8 @@ import android.util.AttributeSet
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.preference.Preference
import com.wireguard.android.QuickTileService
import com.wireguard.android.R
import org.amnezia.awg.QuickTileService
import org.amnezia.awg.R
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
class QuickTilePreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {

View File

@@ -2,15 +2,15 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.preference
package org.amnezia.awg.preference
import android.content.Context
import android.util.AttributeSet
import androidx.preference.Preference
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.util.ToolsInstaller
import com.wireguard.android.util.lifecycleScope
import org.amnezia.awg.Application
import org.amnezia.awg.R
import org.amnezia.awg.util.ToolsInstaller
import org.amnezia.awg.util.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.preference
package org.amnezia.awg.preference
import android.content.Context
import android.content.Intent
@@ -10,14 +10,14 @@ import android.net.Uri
import android.util.AttributeSet
import android.widget.Toast
import androidx.preference.Preference
import com.wireguard.android.Application
import com.wireguard.android.BuildConfig
import com.wireguard.android.R
import com.wireguard.android.backend.Backend
import com.wireguard.android.backend.GoBackend
import com.wireguard.android.backend.WgQuickBackend
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.lifecycleScope
import org.amnezia.awg.Application
import org.amnezia.awg.BuildConfig
import org.amnezia.awg.R
import org.amnezia.awg.backend.Backend
import org.amnezia.awg.backend.GoBackend
import org.amnezia.awg.backend.AwgQuickBackend
import org.amnezia.awg.util.ErrorMessages
import org.amnezia.awg.util.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -31,7 +31,7 @@ class VersionPreference(context: Context, attrs: AttributeSet?) : Preference(con
override fun onClick() {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.wireguard.com/")
intent.data = Uri.parse("https://amnezia.org/")
try {
context.startActivity(intent)
} catch (e: Throwable) {
@@ -41,7 +41,7 @@ class VersionPreference(context: Context, attrs: AttributeSet?) : Preference(con
companion object {
private fun getBackendPrettyName(context: Context, backend: Backend) = when (backend) {
is WgQuickBackend -> context.getString(R.string.type_name_kernel_module)
is AwgQuickBackend -> context.getString(R.string.type_name_kernel_module)
is GoBackend -> context.getString(R.string.type_name_go_userspace)
else -> ""
}

View File

@@ -2,21 +2,21 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.preference
package org.amnezia.awg.preference
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import androidx.preference.Preference
import com.google.android.material.snackbar.Snackbar
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.util.AdminKnobs
import com.wireguard.android.util.BiometricAuthenticator
import com.wireguard.android.util.DownloadsFileSaver
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.activity
import com.wireguard.android.util.lifecycleScope
import org.amnezia.awg.Application
import org.amnezia.awg.R
import org.amnezia.awg.util.AdminKnobs
import org.amnezia.awg.util.BiometricAuthenticator
import org.amnezia.awg.util.DownloadsFileSaver
import org.amnezia.awg.util.ErrorMessages
import org.amnezia.awg.util.activity
import org.amnezia.awg.util.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
@@ -43,7 +43,7 @@ class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference
if (configs.isEmpty()) {
throw IllegalArgumentException(context.getString(R.string.no_tunnels_error))
}
val outputFile = downloadsFileSaver.save("wireguard-export.zip", "application/zip", true)
val outputFile = downloadsFileSaver.save("amneziawg-export.zip", "application/zip", true)
if (outputFile == null) {
withContext(Dispatchers.Main.immediate) {
isEnabled = true
@@ -54,7 +54,7 @@ class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference
ZipOutputStream(outputFile.outputStream).use { zip ->
for (i in configs.indices) {
zip.putNextEntry(ZipEntry(tunnels[i].name + ".conf"))
zip.write(configs[i].toWgQuickString().toByteArray(StandardCharsets.UTF_8))
zip.write(configs[i].toAwgQuickString().toByteArray(StandardCharsets.UTF_8))
}
zip.closeEntry()
}
@@ -108,6 +108,6 @@ class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference
}
companion object {
private const val TAG = "WireGuard/ZipExporterPreference"
private const val TAG = "AmneziaWG/ZipExporterPreference"
}
}

View File

@@ -3,11 +3,11 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
package org.amnezia.awg.util
import android.content.RestrictionsManager
import androidx.core.content.getSystemService
import com.wireguard.android.Application
import org.amnezia.awg.Application
object AdminKnobs {
private val restrictions: RestrictionsManager? = Application.get().getSystemService()

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
package org.amnezia.awg.util
import android.os.Handler
import android.os.Looper
@@ -13,11 +13,11 @@ import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators
import androidx.biometric.BiometricPrompt
import androidx.fragment.app.Fragment
import com.wireguard.android.R
import org.amnezia.awg.R
object BiometricAuthenticator {
private const val TAG = "WireGuard/BiometricAuthenticator"
private const val TAG = "AmneziaWG/BiometricAuthenticator"
// Not all devices support strong biometric auth so we're allowing both device credentials as
// well as weak biometrics.

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
package org.amnezia.awg.util
import android.content.ClipData
import android.content.ClipboardManager
@@ -12,7 +12,7 @@ import android.widget.TextView
import androidx.core.content.getSystemService
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputEditText
import com.wireguard.android.R
import org.amnezia.awg.R
/**
* Standalone utilities for interacting with the system clipboard.

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
package org.amnezia.awg.util
import android.Manifest
import android.content.ContentValues
@@ -17,7 +17,7 @@ import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import com.wireguard.android.R
import org.amnezia.awg.R
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

View File

@@ -2,22 +2,22 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
package org.amnezia.awg.util
import android.content.res.Resources
import android.os.RemoteException
import com.google.zxing.ChecksumException
import com.google.zxing.NotFoundException
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.backend.BackendException
import com.wireguard.android.util.RootShell.RootShellException
import com.wireguard.config.BadConfigException
import com.wireguard.config.InetEndpoint
import com.wireguard.config.InetNetwork
import com.wireguard.config.ParseException
import com.wireguard.crypto.Key
import com.wireguard.crypto.KeyFormatException
import org.amnezia.awg.Application
import org.amnezia.awg.R
import org.amnezia.awg.backend.BackendException
import org.amnezia.awg.util.RootShell.RootShellException
import org.amnezia.awg.config.BadConfigException
import org.amnezia.awg.config.InetEndpoint
import org.amnezia.awg.config.InetNetwork
import org.amnezia.awg.config.ParseException
import org.amnezia.awg.crypto.Key
import org.amnezia.awg.crypto.KeyFormatException
import java.net.InetAddress
object ErrorMessages {
@@ -33,7 +33,7 @@ object ErrorMessages {
)
private val BE_REASON_MAP = mapOf(
BackendException.Reason.UNKNOWN_KERNEL_MODULE_NAME to R.string.module_version_error,
BackendException.Reason.WG_QUICK_CONFIG_ERROR_CODE to R.string.tunnel_config_error,
BackendException.Reason.AWG_QUICK_CONFIG_ERROR_CODE to R.string.tunnel_config_error,
BackendException.Reason.TUNNEL_MISSING_CONFIG to R.string.no_config_error,
BackendException.Reason.VPN_NOT_AUTHORIZED to R.string.vpn_not_authorized_error,
BackendException.Reason.UNABLE_TO_START_VPN to R.string.vpn_start_error,

View File

@@ -3,15 +3,15 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
package org.amnezia.awg.util
import android.content.Context
import android.util.TypedValue
import androidx.annotation.AttrRes
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import com.wireguard.android.Application
import com.wireguard.android.activity.SettingsActivity
import org.amnezia.awg.Application
import org.amnezia.awg.activity.SettingsActivity
import kotlinx.coroutines.CoroutineScope
fun Context.resolveAttribute(@AttrRes attrRes: Int): Int {

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
package org.amnezia.awg.util
import android.content.ContentResolver
import android.graphics.Bitmap

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
package org.amnezia.awg.util
import android.icu.text.ListFormatter
import android.icu.text.MeasureFormat
@@ -11,8 +11,8 @@ import android.icu.text.RelativeDateTimeFormatter
import android.icu.util.Measure
import android.icu.util.MeasureUnit
import android.os.Build
import com.wireguard.android.Application
import com.wireguard.android.R
import org.amnezia.awg.Application
import org.amnezia.awg.R
import java.util.Locale
import kotlin.time.Duration.Companion.seconds
@@ -31,9 +31,6 @@ object QuantityFormatter {
fun formatEpochAgo(epochMillis: Long): String {
var span = (System.currentTimeMillis() - epochMillis) / 1000
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
return Application.get().applicationContext.getString(R.string.latest_handshake_ago, span.seconds.toString())
if (span <= 0L)
return RelativeDateTimeFormatter.getInstance().format(RelativeDateTimeFormatter.Direction.PLAIN, RelativeDateTimeFormatter.AbsoluteUnit.NOW)
val measureFormat = MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)

View File

@@ -3,18 +3,18 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
package org.amnezia.awg.util
import android.content.ContentResolver
import android.net.Uri
import android.provider.OpenableColumns
import android.util.Log
import androidx.fragment.app.FragmentManager
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.fragment.ConfigNamingDialogFragment
import com.wireguard.android.model.ObservableTunnel
import com.wireguard.config.Config
import org.amnezia.awg.Application
import org.amnezia.awg.R
import org.amnezia.awg.fragment.ConfigNamingDialogFragment
import org.amnezia.awg.model.ObservableTunnel
import org.amnezia.awg.config.Config
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@@ -148,5 +148,5 @@ object TunnelImporter {
messageCallback(message)
}
private const val TAG = "WireGuard/TunnelImporter"
private const val TAG = "AmneziaWG/TunnelImporter"
}

View File

@@ -3,13 +3,13 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
package org.amnezia.awg.util
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.core.stringSetPreferencesKey
import com.wireguard.android.Application
import org.amnezia.awg.Application
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@@ -88,34 +88,4 @@ object UserKnobs {
it[RUNNING_TUNNELS] = runningTunnels
}
}
private val UPDATER_NEWER_VERSION_SEEN = stringPreferencesKey("updater_newer_version_seen")
val updaterNewerVersionSeen: Flow<String?>
get() = Application.getPreferencesDataStore().data.map {
it[UPDATER_NEWER_VERSION_SEEN]
}
suspend fun setUpdaterNewerVersionSeen(newerVersionSeen: String?) {
Application.getPreferencesDataStore().edit {
if (newerVersionSeen == null)
it.remove(UPDATER_NEWER_VERSION_SEEN)
else
it[UPDATER_NEWER_VERSION_SEEN] = newerVersionSeen
}
}
private val UPDATER_NEWER_VERSION_CONSENTED = stringPreferencesKey("updater_newer_version_consented")
val updaterNewerVersionConsented: Flow<String?>
get() = Application.getPreferencesDataStore().data.map {
it[UPDATER_NEWER_VERSION_CONSENTED]
}
suspend fun setUpdaterNewerVersionConsented(newerVersionConsented: String?) {
Application.getPreferencesDataStore().edit {
if (newerVersionConsented == null)
it.remove(UPDATER_NEWER_VERSION_CONSENTED)
else
it[UPDATER_NEWER_VERSION_CONSENTED] = newerVersionConsented
}
}
}

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.viewmodel
package org.amnezia.awg.viewmodel
import android.os.Build
import android.os.Parcel
@@ -10,9 +10,9 @@ import android.os.Parcelable
import androidx.core.os.ParcelCompat
import androidx.databinding.ObservableArrayList
import androidx.databinding.ObservableList
import com.wireguard.config.BadConfigException
import com.wireguard.config.Config
import com.wireguard.config.Peer
import org.amnezia.awg.config.BadConfigException
import org.amnezia.awg.config.Config
import org.amnezia.awg.config.Peer
class ConfigProxy : Parcelable {
val `interface`: InterfaceProxy

View File

@@ -0,0 +1,241 @@
/*
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package org.amnezia.awg.viewmodel
import android.os.Parcel
import android.os.Parcelable
import androidx.databinding.BaseObservable
import androidx.databinding.Bindable
import androidx.databinding.ObservableArrayList
import androidx.databinding.ObservableList
import org.amnezia.awg.BR
import org.amnezia.awg.config.Attribute
import org.amnezia.awg.config.BadConfigException
import org.amnezia.awg.config.Interface
import org.amnezia.awg.crypto.Key
import org.amnezia.awg.crypto.KeyFormatException
import org.amnezia.awg.crypto.KeyPair
class InterfaceProxy : BaseObservable, Parcelable {
@get:Bindable
val excludedApplications: ObservableList<String> = ObservableArrayList()
@get:Bindable
val includedApplications: ObservableList<String> = ObservableArrayList()
@get:Bindable
var addresses: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.addresses)
}
@get:Bindable
var dnsServers: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.dnsServers)
}
@get:Bindable
var listenPort: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.listenPort)
}
@get:Bindable
var mtu: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.mtu)
}
@get:Bindable
var junkPacketCount: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.junkPacketCount)
}
@get:Bindable
var junkPacketMinSize: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.junkPacketMinSize)
}
@get:Bindable
var junkPacketMaxSize: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.junkPacketMaxSize)
}
@get:Bindable
var initPacketJunkSize: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.initPacketJunkSize)
}
@get:Bindable
var responsePacketJunkSize: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.responsePacketJunkSize)
}
@get:Bindable
var initPacketMagicHeader: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.initPacketMagicHeader)
}
@get:Bindable
var responsePacketMagicHeader: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.responsePacketMagicHeader)
}
@get:Bindable
var underloadPacketMagicHeader: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.underloadPacketMagicHeader)
}
@get:Bindable
var transportPacketMagicHeader: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.transportPacketMagicHeader)
}
@get:Bindable
var privateKey: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.privateKey)
notifyPropertyChanged(BR.publicKey)
}
@get:Bindable
val publicKey: String
get() = try {
KeyPair(Key.fromBase64(privateKey)).publicKey.toBase64()
} catch (ignored: KeyFormatException) {
""
}
private constructor(parcel: Parcel) {
addresses = parcel.readString() ?: ""
dnsServers = parcel.readString() ?: ""
parcel.readStringList(excludedApplications)
parcel.readStringList(includedApplications)
listenPort = parcel.readString() ?: ""
mtu = parcel.readString() ?: ""
junkPacketCount = parcel.readString() ?: ""
junkPacketMinSize = parcel.readString() ?: ""
junkPacketMaxSize = parcel.readString() ?: ""
initPacketJunkSize = parcel.readString() ?: ""
responsePacketJunkSize = parcel.readString() ?: ""
initPacketMagicHeader = parcel.readString() ?: ""
responsePacketMagicHeader = parcel.readString() ?: ""
underloadPacketMagicHeader = parcel.readString() ?: ""
transportPacketMagicHeader = parcel.readString() ?: ""
privateKey = parcel.readString() ?: ""
}
constructor(other: Interface) {
addresses = Attribute.join(other.addresses)
val dnsServerStrings = other.dnsServers.map { it.hostAddress }.plus(other.dnsSearchDomains)
dnsServers = Attribute.join(dnsServerStrings)
excludedApplications.addAll(other.excludedApplications)
includedApplications.addAll(other.includedApplications)
listenPort = other.listenPort.map { it.toString() }.orElse("")
mtu = other.mtu.map { it.toString() }.orElse("")
junkPacketCount = other.junkPacketCount.map { it.toString() }.orElse("")
junkPacketMinSize = other.junkPacketMinSize.map { it.toString() }.orElse("")
junkPacketMaxSize = other.junkPacketMaxSize.map { it.toString() }.orElse("")
initPacketJunkSize = other.initPacketJunkSize.map { it.toString() }.orElse("")
responsePacketJunkSize = other.responsePacketJunkSize.map { it.toString() }.orElse("")
initPacketMagicHeader = other.initPacketMagicHeader.map { it.toString() }.orElse("")
responsePacketMagicHeader = other.responsePacketMagicHeader.map { it.toString() }.orElse("")
underloadPacketMagicHeader = other.underloadPacketMagicHeader.map { it.toString() }.orElse("")
transportPacketMagicHeader = other.transportPacketMagicHeader.map { it.toString() }.orElse("")
val keyPair = other.keyPair
privateKey = keyPair.privateKey.toBase64()
}
constructor()
override fun describeContents() = 0
fun generateKeyPair() {
val keyPair = KeyPair()
privateKey = keyPair.privateKey.toBase64()
notifyPropertyChanged(BR.privateKey)
notifyPropertyChanged(BR.publicKey)
}
@Throws(BadConfigException::class)
fun resolve(): Interface {
val builder = Interface.Builder()
if (addresses.isNotEmpty()) builder.parseAddresses(addresses)
if (dnsServers.isNotEmpty()) builder.parseDnsServers(dnsServers)
if (excludedApplications.isNotEmpty()) builder.excludeApplications(excludedApplications)
if (includedApplications.isNotEmpty()) builder.includeApplications(includedApplications)
if (listenPort.isNotEmpty()) builder.parseListenPort(listenPort)
if (mtu.isNotEmpty()) builder.parseMtu(mtu)
if (junkPacketCount.isNotEmpty()) builder.parseJunkPacketCount(junkPacketCount)
if (junkPacketMinSize.isNotEmpty()) builder.parseJunkPacketMinSize(junkPacketMinSize)
if (junkPacketMaxSize.isNotEmpty()) builder.parseJunkPacketMaxSize(junkPacketMaxSize)
if (initPacketJunkSize.isNotEmpty()) builder.parseInitPacketJunkSize(initPacketJunkSize)
if (responsePacketJunkSize.isNotEmpty()) builder.parseResponsePacketJunkSize(responsePacketJunkSize)
if (initPacketMagicHeader.isNotEmpty()) builder.parseInitPacketMagicHeader(initPacketMagicHeader)
if (responsePacketMagicHeader.isNotEmpty()) builder.parseResponsePacketMagicHeader(responsePacketMagicHeader)
if (underloadPacketMagicHeader.isNotEmpty()) builder.parseUnderloadPacketMagicHeader(underloadPacketMagicHeader)
if (transportPacketMagicHeader.isNotEmpty()) builder.parseTransportPacketMagicHeader(transportPacketMagicHeader)
if (privateKey.isNotEmpty()) builder.parsePrivateKey(privateKey)
return builder.build()
}
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(addresses)
dest.writeString(dnsServers)
dest.writeStringList(excludedApplications)
dest.writeStringList(includedApplications)
dest.writeString(listenPort)
dest.writeString(mtu)
dest.writeString(junkPacketCount)
dest.writeString(junkPacketMinSize)
dest.writeString(junkPacketMaxSize)
dest.writeString(initPacketJunkSize)
dest.writeString(responsePacketJunkSize)
dest.writeString(initPacketMagicHeader)
dest.writeString(responsePacketMagicHeader)
dest.writeString(underloadPacketMagicHeader)
dest.writeString(transportPacketMagicHeader)
dest.writeString(privateKey)
}
private class InterfaceProxyCreator : Parcelable.Creator<InterfaceProxy> {
override fun createFromParcel(parcel: Parcel): InterfaceProxy {
return InterfaceProxy(parcel)
}
override fun newArray(size: Int): Array<InterfaceProxy?> {
return arrayOfNulls(size)
}
}
companion object {
@JvmField
val CREATOR: Parcelable.Creator<InterfaceProxy> = InterfaceProxyCreator()
}
}

View File

@@ -2,7 +2,7 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.viewmodel
package org.amnezia.awg.viewmodel
import android.os.Parcel
import android.os.Parcelable
@@ -11,10 +11,10 @@ import androidx.databinding.Bindable
import androidx.databinding.Observable
import androidx.databinding.Observable.OnPropertyChangedCallback
import androidx.databinding.ObservableList
import com.wireguard.android.BR
import com.wireguard.config.Attribute
import com.wireguard.config.BadConfigException
import com.wireguard.config.Peer
import org.amnezia.awg.BR
import org.amnezia.awg.config.Attribute
import org.amnezia.awg.config.BadConfigException
import org.amnezia.awg.config.Peer
import java.lang.ref.WeakReference
class PeerProxy : BaseObservable, Parcelable {

View File

@@ -2,15 +2,15 @@
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.widget
package org.amnezia.awg.widget
import android.text.InputFilter
import android.text.SpannableStringBuilder
import android.text.Spanned
import com.wireguard.crypto.Key
import org.amnezia.awg.crypto.Key
/**
* InputFilter for entering WireGuard private/public keys encoded with base64.
* InputFilter for entering AmneziaWG private/public keys encoded with base64.
*/
class KeyInputFilter : InputFilter {
override fun filter(

Some files were not shown because too many files have changed in this diff Show More