diff options
Diffstat (limited to 'app')
37 files changed, 2 insertions, 4141 deletions
diff --git a/app/build.gradle b/app/build.gradle index a32d14f0..a21c2027 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -65,51 +65,21 @@ android { } } } - externalNativeBuild { - cmake { - path 'tools/CMakeLists.txt' - } - } -} - -ext { - annotationsVersion = '1.1.0' - appcompatVersion = '1.1.0' - cardviewVersion = '1.0.0' - coreKtxVersion = '1.2.0' - constraintLayoutVersion = '1.1.3' - coordinatorLayoutVersion = '1.1.0' - databindingVersion = '3.6.0' - fragmentVersion = '1.2.2' - materialComponentsVersion = '1.1.0' - jsr305Version = '3.0.2' - kotlinVersion = '1.3.70' - preferenceVersion = '1.1.0' - streamsupportVersion = '1.7.1' - threetenabpVersion = '1.2.2' - // ZXING switched minSdk to 24 so we cannot upgrade to 4.0.2 without following suit. - // If you choose to upgrade to minSDK 24 then you should also disable Jetifier from - // gradle.properties. - zxingEmbeddedVersion = '3.6.0' - eddsaVersion = '0.3.0' } dependencies { + implementation project(":tunnel") implementation "androidx.annotation:annotation:$annotationsVersion" implementation "androidx.appcompat:appcompat:$appcompatVersion" implementation "androidx.cardview:cardview:$cardviewVersion" implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion" implementation "androidx.coordinatorlayout:coordinatorlayout:$coordinatorLayoutVersion" implementation "androidx.core:core-ktx:$coreKtxVersion" - implementation "androidx.databinding:databinding-runtime:$databindingVersion" + implementation "androidx.databinding:databinding-runtime:$agpVersion" implementation "androidx.fragment:fragment:$fragmentVersion" implementation "androidx.preference:preference:$preferenceVersion" implementation "com.google.android.material:material:$materialComponentsVersion" - implementation "com.google.code.findbugs:jsr305:$jsr305Version" - implementation "com.jakewharton.threetenabp:threetenabp:$threetenabpVersion" implementation "com.journeyapps:zxing-android-embedded:$zxingEmbeddedVersion" - implementation "net.sourceforge.streamsupport:android-retrofuture:$streamsupportVersion" - implementation "net.sourceforge.streamsupport:android-retrostreams:$streamsupportVersion" implementation "net.i2p.crypto:eddsa:$eddsaVersion" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4bcb7588..5e993ae2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -74,14 +74,6 @@ </receiver> <service - android:name=".backend.GoBackend$VpnService" - android:permission="android.permission.BIND_VPN_SERVICE"> - <intent-filter> - <action android:name="android.net.VpnService" /> - </intent-filter> - </service> - - <service android:name=".QuickTileService" android:icon="@drawable/ic_tile" android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"> diff --git a/app/src/main/java/com/wireguard/android/backend/Backend.java b/app/src/main/java/com/wireguard/android/backend/Backend.java deleted file mode 100644 index ed3a5ebd..00000000 --- a/app/src/main/java/com/wireguard/android/backend/Backend.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.android.backend; - -import com.wireguard.config.Config; - -import java.util.Collection; -import java.util.Set; - -import androidx.annotation.Nullable; - -/** - * Interface for implementations of the WireGuard secure network tunnel. - */ - -public interface Backend { - /** - * Enumerate names of currently-running tunnels. - * - * @return The set of running tunnel names. - */ - Set<String> getRunningTunnelNames(); - - /** - * Get the state of a tunnel. - * - * @param tunnel The tunnel to examine the state of. - * @return The state of the tunnel. - */ - Tunnel.State getState(Tunnel tunnel) throws Exception; - - /** - * Get statistics about traffic and errors on this tunnel. If the tunnel is not running, the - * statistics object will be filled with zero values. - * - * @param tunnel The tunnel to retrieve statistics for. - * @return The statistics for the tunnel. - */ - Statistics getStatistics(Tunnel tunnel) throws Exception; - - /** - * Determine version of underlying backend. - * - * @return The version of the backend. - * @throws Exception - */ - String getVersion() throws Exception; - - /** - * Set the state of a tunnel, updating it's configuration. If the tunnel is already up, config - * may update the running configuration; config may be null when setting the tunnel down. - * - * @param tunnel The tunnel to control the state of. - * @param state The new state for this tunnel. Must be {@code UP}, {@code DOWN}, or - * {@code TOGGLE}. - * @param config The configuration for this tunnel, may be null if state is {@code DOWN}. - * @return The updated state of the tunnel. - */ - Tunnel.State setState(Tunnel tunnel, Tunnel.State state, @Nullable Config config) throws Exception; -} diff --git a/app/src/main/java/com/wireguard/android/backend/BackendException.java b/app/src/main/java/com/wireguard/android/backend/BackendException.java deleted file mode 100644 index e1e8eaa9..00000000 --- a/app/src/main/java/com/wireguard/android/backend/BackendException.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright © 2020 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.android.backend; - -public final class BackendException extends Exception { - public enum Reason { - UNKNOWN_KERNEL_MODULE_NAME, - WG_QUICK_CONFIG_ERROR_CODE, - TUNNEL_MISSING_CONFIG, - VPN_NOT_AUTHORIZED, - UNABLE_TO_START_VPN, - TUN_CREATION_ERROR, - GO_ACTIVATION_ERROR_CODE - } - private final Reason reason; - private final Object[] format; - public BackendException(final Reason reason, final Object ...format) { - this.reason = reason; - this.format = format; - } - public Reason getReason() { - return reason; - } - public Object[] getFormat() { - return format; - } -} diff --git a/app/src/main/java/com/wireguard/android/backend/GoBackend.java b/app/src/main/java/com/wireguard/android/backend/GoBackend.java deleted file mode 100644 index 6ad5afa4..00000000 --- a/app/src/main/java/com/wireguard/android/backend/GoBackend.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.android.backend; - -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.ParcelFileDescriptor; -import androidx.annotation.Nullable; -import androidx.collection.ArraySet; -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.InetNetwork; -import com.wireguard.config.Peer; -import com.wireguard.crypto.Key; -import com.wireguard.crypto.KeyFormatException; - -import java.net.InetAddress; -import java.util.Collections; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import java9.util.concurrent.CompletableFuture; - -public final class GoBackend implements Backend { - private static final String TAG = "WireGuard/" + GoBackend.class.getSimpleName(); - private static CompletableFuture<VpnService> vpnService = new CompletableFuture<>(); - public interface AlwaysOnCallback { - void alwaysOnTriggered(); - } - @Nullable private static AlwaysOnCallback alwaysOnCallback; - public static void setAlwaysOnCallback(AlwaysOnCallback cb) { - alwaysOnCallback = cb; - } - - private final Context context; - @Nullable private Tunnel currentTunnel; - @Nullable private Config currentConfig; - private int currentTunnelHandle = -1; - - public GoBackend(final Context context) { - SharedLibraryLoader.loadSharedLibrary(context, "wg-go"); - this.context = context; - } - - 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(); - - @Override - public Set<String> getRunningTunnelNames() { - if (currentTunnel != null) { - final Set<String> runningTunnels = new ArraySet<>(); - runningTunnels.add(currentTunnel.getName()); - return runningTunnels; - } - return Collections.emptySet(); - } - - @Override - public State getState(final Tunnel tunnel) { - return currentTunnel == tunnel ? State.UP : State.DOWN; - } - - @Override - public Statistics getStatistics(final Tunnel tunnel) { - final Statistics stats = new Statistics(); - if (tunnel != currentTunnel) { - return stats; - } - final String config = wgGetConfig(currentTunnelHandle); - Key key = null; - long rx = 0, tx = 0; - for (final String line : config.split("\\n")) { - if (line.startsWith("public_key=")) { - if (key != null) - stats.add(key, rx, tx); - rx = 0; - tx = 0; - try { - key = Key.fromHex(line.substring(11)); - } catch (final KeyFormatException ignored) { - key = null; - } - } else if (line.startsWith("rx_bytes=")) { - if (key == null) - continue; - try { - rx = Long.parseLong(line.substring(9)); - } catch (final NumberFormatException ignored) { - rx = 0; - } - } else if (line.startsWith("tx_bytes=")) { - if (key == null) - continue; - try { - tx = Long.parseLong(line.substring(9)); - } catch (final NumberFormatException ignored) { - tx = 0; - } - } - } - if (key != null) - stats.add(key, rx, tx); - return stats; - } - - @Override - public String getVersion() { - return wgVersion(); - } - - @Override - public State setState(final Tunnel tunnel, State state, @Nullable final Config config) throws Exception { - final State originalState = getState(tunnel); - - if (state == State.TOGGLE) - state = originalState == State.UP ? State.DOWN : State.UP; - if (state == originalState && tunnel == currentTunnel && config == currentConfig) - return originalState; - if (state == State.UP) { - final Config originalConfig = currentConfig; - final Tunnel originalTunnel = currentTunnel; - if (currentTunnel != null) - setStateInternal(currentTunnel, null, State.DOWN); - try { - setStateInternal(tunnel, config, state); - } catch(final Exception e) { - if (originalTunnel != null) - setStateInternal(originalTunnel, originalConfig, State.UP); - throw e; - } - } else if (state == State.DOWN && tunnel == currentTunnel) { - setStateInternal(tunnel, null, State.DOWN); - } - return getState(tunnel); - } - - private void setStateInternal(final Tunnel tunnel, @Nullable final Config config, final State state) - throws Exception { - Log.i(TAG, "Bringing tunnel " + tunnel.getName() + " " + state); - - if (state == State.UP) { - if (config == null) - throw new BackendException(Reason.TUNNEL_MISSING_CONFIG); - - if (VpnService.prepare(context) != null) - throw new BackendException(Reason.VPN_NOT_AUTHORIZED); - - final VpnService service; - if (!vpnService.isDone()) - startVpnService(); - - try { - service = vpnService.get(2, TimeUnit.SECONDS); - } catch (final TimeoutException e) { - final Exception be = new BackendException(Reason.UNABLE_TO_START_VPN); - be.initCause(e); - throw be; - } - service.setOwner(this); - - if (currentTunnelHandle != -1) { - Log.w(TAG, "Tunnel already up"); - return; - } - - // Build config - final String goConfig = config.toWgUserspaceString(); - - // Create the vpn tunnel with android API - final VpnService.Builder builder = service.getBuilder(); - builder.setSession(tunnel.getName()); - - for (final String excludedApplication : config.getInterface().getExcludedApplications()) - builder.addDisallowedApplication(excludedApplication); - - for (final InetNetwork addr : config.getInterface().getAddresses()) - builder.addAddress(addr.getAddress(), addr.getMask()); - - for (final InetAddress addr : config.getInterface().getDnsServers()) - builder.addDnsServer(addr.getHostAddress()); - - for (final Peer peer : config.getPeers()) { - for (final InetNetwork addr : peer.getAllowedIps()) - builder.addRoute(addr.getAddress(), addr.getMask()); - } - - builder.setMtu(config.getInterface().getMtu().orElse(1280)); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) - builder.setMetered(false); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) - service.setUnderlyingNetworks(null); - - builder.setBlocking(true); - try (final ParcelFileDescriptor tun = builder.establish()) { - if (tun == null) - throw new BackendException(Reason.TUN_CREATION_ERROR); - Log.d(TAG, "Go backend v" + wgVersion()); - currentTunnelHandle = wgTurnOn(tunnel.getName(), tun.detachFd(), goConfig); - } - if (currentTunnelHandle < 0) - throw new BackendException(Reason.GO_ACTIVATION_ERROR_CODE, currentTunnelHandle); - - currentTunnel = tunnel; - currentConfig = config; - - service.protect(wgGetSocketV4(currentTunnelHandle)); - service.protect(wgGetSocketV6(currentTunnelHandle)); - } else { - if (currentTunnelHandle == -1) { - Log.w(TAG, "Tunnel already down"); - return; - } - - wgTurnOff(currentTunnelHandle); - currentTunnel = null; - currentTunnelHandle = -1; - currentConfig = null; - } - - tunnel.onStateChange(state); - } - - private void startVpnService() { - Log.d(TAG, "Requesting to start VpnService"); - context.startService(new Intent(context, VpnService.class)); - } - - public static class VpnService extends android.net.VpnService { - @Nullable private GoBackend owner; - - public void setOwner(final GoBackend owner) { - this.owner = owner; - } - - public Builder getBuilder() { - return new Builder(); - } - - @Override - public void onCreate() { - vpnService.complete(this); - super.onCreate(); - } - - @Override - public void onDestroy() { - if (owner != null) { - final Tunnel tunnel = owner.currentTunnel; - if (tunnel != null) { - if (owner.currentTunnelHandle != -1) - wgTurnOff(owner.currentTunnelHandle); - owner.currentTunnel = null; - owner.currentTunnelHandle = -1; - owner.currentConfig = null; - tunnel.onStateChange(State.DOWN); - } - } - vpnService = vpnService.newIncompleteFuture(); - super.onDestroy(); - } - - @Override - public int onStartCommand(@Nullable final Intent intent, final int flags, final int startId) { - vpnService.complete(this); - if (intent == null || intent.getComponent() == null || !intent.getComponent().getPackageName().equals(getPackageName())) { - Log.d(TAG, "Service started by Always-on VPN feature"); - if (alwaysOnCallback != null) - alwaysOnCallback.alwaysOnTriggered(); - } - return super.onStartCommand(intent, flags, startId); - } - } -} diff --git a/app/src/main/java/com/wireguard/android/backend/Statistics.java b/app/src/main/java/com/wireguard/android/backend/Statistics.java deleted file mode 100644 index 2ca87d23..00000000 --- a/app/src/main/java/com/wireguard/android/backend/Statistics.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright © 2020 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.android.backend; - -import android.os.SystemClock; -import android.util.Pair; - -import com.wireguard.crypto.Key; - -import java.util.HashMap; -import java.util.Map; - -public class Statistics { - private long lastTouched = SystemClock.elapsedRealtime(); - private final Map<Key, Pair<Long, Long>> peerBytes = new HashMap<>(); - - Statistics() { } - - void add(final Key key, final long rx, final long tx) { - peerBytes.put(key, Pair.create(rx, tx)); - lastTouched = SystemClock.elapsedRealtime(); - } - - public boolean isStale() { - return SystemClock.elapsedRealtime() - lastTouched > 900; - } - - public Key[] peers() { - return peerBytes.keySet().toArray(new Key[0]); - } - - public long peerRx(final Key peer) { - if (!peerBytes.containsKey(peer)) - return 0; - return peerBytes.get(peer).first; - } - - public long peerTx(final Key peer) { - if (!peerBytes.containsKey(peer)) - return 0; - return peerBytes.get(peer).second; - } - - public long totalRx() { - long rx = 0; - for (final Pair<Long, Long> val : peerBytes.values()) { - rx += val.first; - } - return rx; - } - - public long totalTx() { - long tx = 0; - for (final Pair<Long, Long> val : peerBytes.values()) { - tx += val.second; - } - return tx; - } -} diff --git a/app/src/main/java/com/wireguard/android/backend/Tunnel.java b/app/src/main/java/com/wireguard/android/backend/Tunnel.java deleted file mode 100644 index af2f59f7..00000000 --- a/app/src/main/java/com/wireguard/android/backend/Tunnel.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © 2020 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.android.backend; - -import java.util.regex.Pattern; - -/** - * Represents a WireGuard tunnel. - */ - -public interface Tunnel { - enum State { - DOWN, - TOGGLE, - UP; - - public static State of(final boolean running) { - return running ? UP : DOWN; - } - } - - int NAME_MAX_LENGTH = 15; - Pattern NAME_PATTERN = Pattern.compile("[a-zA-Z0-9_=+.-]{1,15}"); - - static boolean isNameInvalid(final CharSequence name) { - return !NAME_PATTERN.matcher(name).matches(); - } - - /** - * Get the name of the tunnel, which should always pass the !isNameInvalid test. - * - * @return The name of the tunnel. - */ - String getName(); - - /** - * React to a change in state of the tunnel. Should only be directly called by Backend. - * - * @param newState The new state of the tunnel. - * @return The new state of the tunnel. - */ - void onStateChange(State newState); -} diff --git a/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java b/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java deleted file mode 100644 index 9695aab7..00000000 --- a/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.android.backend; - -import androidx.annotation.Nullable; - -import android.content.Context; -import android.util.Log; - -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 java.io.File; -import java.io.FileOutputStream; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.HashMap; - -import java9.util.stream.Collectors; -import java9.util.stream.Stream; - -/** - * WireGuard backend that uses {@code wg-quick} to implement tunnel configuration. - */ - -public final class WgQuickBackend implements Backend { - private static final String TAG = "WireGuard/" + WgQuickBackend.class.getSimpleName(); - - private final RootShell rootShell; - private final ToolsInstaller toolsInstaller; - private final File localTemporaryDir; - private final Map<Tunnel, Config> runningConfigs = new HashMap<>(); - - public WgQuickBackend(final Context context, final RootShell rootShell, final ToolsInstaller toolsInstaller) { - localTemporaryDir = new File(context.getCacheDir(), "tmp"); - this.rootShell = rootShell; - this.toolsInstaller = toolsInstaller; - } - - @Override - public Set<String> getRunningTunnelNames() { - final List<String> output = new ArrayList<>(); - // 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()) - 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. - return Stream.of(output.get(0).split(" ")).collect(Collectors.toUnmodifiableSet()); - } - - @Override - public State getState(final Tunnel tunnel) { - return getRunningTunnelNames().contains(tunnel.getName()) ? State.UP : State.DOWN; - } - - @Override - public Statistics getStatistics(final Tunnel tunnel) { - final Statistics stats = new Statistics(); - final Collection<String> output = new ArrayList<>(); - try { - if (rootShell.run(output, String.format("wg show '%s' transfer", tunnel.getName())) != 0) - return stats; - } catch (final Exception ignored) { - return stats; - } - for (final String line : output) { - final String[] parts = line.split("\\t"); - if (parts.length != 3) - continue; - try { - stats.add(Key.fromBase64(parts[0]), Long.parseLong(parts[1]), Long.parseLong(parts[2])); - } catch (final Exception ignored) { - } - } - return stats; - } - - @Override - public String getVersion() throws Exception { - final List<String> output = new ArrayList<>(); - if (rootShell.run(output, "cat /sys/module/wireguard/version") != 0 || output.isEmpty()) - throw new BackendException(Reason.UNKNOWN_KERNEL_MODULE_NAME); - return output.get(0); - } - - @Override - public State setState(final Tunnel tunnel, State state, @Nullable final Config config) throws Exception { - final State originalState = getState(tunnel); - final Config originalConfig = runningConfigs.get(tunnel); - - if (state == State.TOGGLE) - state = originalState == State.UP ? State.DOWN : State.UP; - if ((state == State.UP && originalState == State.UP && originalConfig != null && originalConfig == config) || - (state == State.DOWN && originalState == State.DOWN)) - return originalState; - if (state == State.UP) { - toolsInstaller.ensureToolsAvailable(); - if (originalState == State.UP) - setStateInternal(tunnel, originalConfig == null ? config : originalConfig, State.DOWN); - try { - setStateInternal(tunnel, config, State.UP); - } catch(final Exception e) { - if (originalState == State.UP && originalConfig != null) - setStateInternal(tunnel, originalConfig, State.UP); - throw e; - } - } else if (state == State.DOWN) { - setStateInternal(tunnel, originalConfig == null ? config : originalConfig, State.DOWN); - } - return state; - } - - private void setStateInternal(final Tunnel tunnel, @Nullable final Config config, final State state) throws Exception { - Log.i(TAG, "Bringing tunnel " + tunnel.getName() + " " + state); - - Objects.requireNonNull(config, "Trying to set state up with a null config"); - - 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)); - } - String command = String.format("wg-quick %s '%s'", - state.toString().toLowerCase(Locale.ENGLISH), tempFile.getAbsolutePath()); - if (state == State.UP) - command = "cat /sys/module/wireguard/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); - - if (state == State.UP) - runningConfigs.put(tunnel, config); - else - runningConfigs.remove(tunnel); - - tunnel.onStateChange(state); - } -} diff --git a/app/src/main/java/com/wireguard/android/util/AsyncWorker.java b/app/src/main/java/com/wireguard/android/util/AsyncWorker.java deleted file mode 100644 index 1d041851..00000000 --- a/app/src/main/java/com/wireguard/android/util/AsyncWorker.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.android.util; - -import android.os.Handler; - -import java.util.concurrent.Executor; - -import java9.util.concurrent.CompletableFuture; -import java9.util.concurrent.CompletionStage; - -/** - * Helper class for running asynchronous tasks and ensuring they are completed on the main thread. - */ - -public class AsyncWorker { - private final Executor executor; - private final Handler handler; - - public AsyncWorker(final Executor executor, final Handler handler) { - this.executor = executor; - this.handler = handler; - } - - public CompletionStage<Void> runAsync(final AsyncRunnable<?> runnable) { - final CompletableFuture<Void> future = new CompletableFuture<>(); - executor.execute(() -> { - try { - runnable.run(); - handler.post(() -> future.complete(null)); - } catch (final Throwable t) { - handler.post(() -> future.completeExceptionally(t)); - } - }); - return future; - } - - public <T> CompletionStage<T> supplyAsync(final AsyncSupplier<T, ?> supplier) { - final CompletableFuture<T> future = new CompletableFuture<>(); - executor.execute(() -> { - try { - final T result = supplier.get(); - handler.post(() -> future.complete(result)); - } catch (final Throwable t) { - handler.post(() -> future.completeExceptionally(t)); - } - }); - return future; - } - - @FunctionalInterface - public interface AsyncRunnable<E extends Throwable> { - void run() throws E; - } - - @FunctionalInterface - public interface AsyncSupplier<T, E extends Throwable> { - T get() throws E; - } -} diff --git a/app/src/main/java/com/wireguard/android/util/RootShell.java b/app/src/main/java/com/wireguard/android/util/RootShell.java deleted file mode 100644 index 1fc2c9f2..00000000 --- a/app/src/main/java/com/wireguard/android/util/RootShell.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.android.util; - -import android.content.Context; -import androidx.annotation.Nullable; -import android.util.Log; - -import com.wireguard.android.util.RootShell.RootShellException.Reason; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.UUID; - -/** - * Helper class for running commands as root. - */ - -public class RootShell { - private static final String SU = "su"; - private static final String TAG = "WireGuard/" + RootShell.class.getSimpleName(); - - private final File localBinaryDir; - private final File localTemporaryDir; - private final Object lock = new Object(); - private final String preamble; - @Nullable private Process process; - @Nullable private BufferedReader stderr; - @Nullable private OutputStreamWriter stdin; - @Nullable private BufferedReader stdout; - - public RootShell(final Context context) { - localBinaryDir = new File(context.getCodeCacheDir(), "bin"); - localTemporaryDir = new File(context.getCacheDir(), "tmp"); - preamble = String.format("export CALLING_PACKAGE=%s PATH=\"%s:$PATH\" TMPDIR='%s'; id -u\n", - context.getPackageName(), localBinaryDir, localTemporaryDir); - } - - private static boolean isExecutableInPath(final String name) { - final String path = System.getenv("PATH"); - if (path == null) - return false; - for (final String dir : path.split(":")) - if (new File(dir, name).canExecute()) - return true; - return false; - } - - private boolean isRunning() { - synchronized (lock) { - try { - // Throws an exception if the process hasn't finished yet. - if (process != null) - process.exitValue(); - return false; - } catch (final IllegalThreadStateException ignored) { - // The existing process is still running. - return true; - } - } - } - - /** - * Run a command in a root shell. - * - * @param output Lines read from stdout are appended to this list. Pass null if the - * output from the shell is not important. - * @param command Command to run as root. - * @return The exit value of the command. - */ - public int run(@Nullable final Collection<String> output, final String command) - throws IOException, RootShellException { - synchronized (lock) { - /* Start inside synchronized block to prevent a concurrent call to stop(). */ - start(); - final String marker = UUID.randomUUID().toString(); - final String script = "echo " + marker + "; echo " + marker + " >&2; (" + command + - "); ret=$?; echo " + marker + " $ret; echo " + marker + " $ret >&2\n"; - Log.v(TAG, "executing: " + command); - stdin.write(script); - stdin.flush(); - String line; - int errnoStdout = Integer.MIN_VALUE; - int errnoStderr = Integer.MAX_VALUE; - int markersSeen = 0; - while ((line = stdout.readLine()) != null) { - if (line.startsWith(marker)) { - ++markersSeen; - if (line.length() > marker.length() + 1) { - errnoStdout = Integer.valueOf(line.substring(marker.length() + 1)); - break; - } - } else if (markersSeen > 0) { - if (output != null) - output.add(line); - Log.v(TAG, "stdout: " + line); - } - } - while ((line = stderr.readLine()) != null) { - if (line.startsWith(marker)) { - ++markersSeen; - if (line.length() > marker.length() + 1) { - errnoStderr = Integer.valueOf(line.substring(marker.length() + 1)); - break; - } - } else if (markersSeen > 2) { - Log.v(TAG, "stderr: " + line); - } - } - if (markersSeen != 4) - throw new RootShellException(Reason.SHELL_MARKER_COUNT_ERROR, markersSeen); - if (errnoStdout != errnoStderr) - throw new RootShellException(Reason.SHELL_EXIT_STATUS_READ_ERROR); - Log.v(TAG, "exit: " + errnoStdout); - return errnoStdout; - } - } - - public void start() throws IOException, RootShellException { - if (!isExecutableInPath(SU)) - throw new RootShellException(Reason.NO_ROOT_ACCESS); - synchronized (lock) { - if (isRunning()) - return; - if (!localBinaryDir.isDirectory() && !localBinaryDir.mkdirs()) - throw new RootShellException(Reason.CREATE_BIN_DIR_ERROR); - if (!localTemporaryDir.isDirectory() && !localTemporaryDir.mkdirs()) - throw new RootShellException(Reason.CREATE_TEMP_DIR_ERROR); - try { - final ProcessBuilder builder = new ProcessBuilder().command(SU); - builder.environment().put("LC_ALL", "C"); - try { - process = builder.start(); - } catch (final IOException e) { - // A failure at this stage means the device isn't rooted. - final RootShellException rse = new RootShellException(Reason.NO_ROOT_ACCESS); - rse.initCause(e); - throw rse; - } - stdin = new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8); - stdout = new BufferedReader(new InputStreamReader(process.getInputStream(), - StandardCharsets.UTF_8)); - stderr = new BufferedReader(new InputStreamReader(process.getErrorStream(), - StandardCharsets.UTF_8)); - stdin.write(preamble); - stdin.flush(); - // Check that the shell started successfully. - final String uid = stdout.readLine(); - if (!"0".equals(uid)) { - Log.w(TAG, "Root check did not return correct UID: " + uid); - throw new RootShellException(Reason.NO_ROOT_ACCESS); - } - if (!isRunning()) { - String line; - while ((line = stderr.readLine()) != null) { - Log.w(TAG, "Root check returned an error: " + line); - if (line.contains("Permission denied")) - throw new RootShellException(Reason.NO_ROOT_ACCESS); - } - throw new RootShellException(Reason.SHELL_START_ERROR, process.exitValue()); - } - } catch (final IOException | RootShellException e) { - stop(); - throw e; - } - } - } - - public void stop() { - synchronized (lock) { - if (process != null) { - process.destroy(); - process = null; - } - } - } - - public static class RootShellException extends Exception { - public enum Reason { - NO_ROOT_ACCESS, - SHELL_MARKER_COUNT_ERROR, - SHELL_EXIT_STATUS_READ_ERROR, - SHELL_START_ERROR, - CREATE_BIN_DIR_ERROR, - CREATE_TEMP_DIR_ERROR - } - private final Reason reason; - private final Object[] format; - public RootShellException(final Reason reason, final Object ...format) { - this.reason = reason; - this.format = format; - } - public boolean isIORelated() { - return reason != Reason.NO_ROOT_ACCESS; - } - public Reason getReason() { - return reason; - } - public Object[] getFormat() { - return format; - } - } -} diff --git a/app/src/main/java/com/wireguard/android/util/SharedLibraryLoader.java b/app/src/main/java/com/wireguard/android/util/SharedLibraryLoader.java deleted file mode 100644 index 93e44b64..00000000 --- a/app/src/main/java/com/wireguard/android/util/SharedLibraryLoader.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.android.util; - -import android.content.Context; -import android.os.Build; -import android.util.Log; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -public final class SharedLibraryLoader { - private static final String TAG = "WireGuard/" + SharedLibraryLoader.class.getSimpleName(); - - private SharedLibraryLoader() { - } - - public static boolean extractLibrary(final Context context, final String libName, final File destination) throws IOException { - final Collection<String> apks = new HashSet<>(); - if (context.getApplicationInfo().sourceDir != null) - apks.add(context.getApplicationInfo().sourceDir); - if (context.getApplicationInfo().splitSourceDirs != null) - apks.addAll(Arrays.asList(context.getApplicationInfo().splitSourceDirs)); - - for (final String abi : Build.SUPPORTED_ABIS) { - for (final String apk : apks) { - final ZipFile zipFile; - try { - zipFile = new ZipFile(new File(apk), ZipFile.OPEN_READ); - } catch (final IOException e) { - throw new RuntimeException(e); - } - - final String mappedLibName = System.mapLibraryName(libName); - final byte[] buffer = new byte[1024 * 32]; - final String libZipPath = "lib" + File.separatorChar + abi + File.separatorChar + mappedLibName; - final ZipEntry zipEntry = zipFile.getEntry(libZipPath); - if (zipEntry == null) - continue; - Log.d(TAG, "Extracting apk:/" + libZipPath + " to " + destination.getAbsolutePath()); - try (final FileOutputStream out = new FileOutputStream(destination); - final InputStream in = zipFile.getInputStream(zipEntry)) { - int len; - while ((len = in.read(buffer)) != -1) { - out.write(buffer, 0, len); - } - out.getFD().sync(); - } - zipFile.close(); - return true; - } - } - return false; - } - - public static void loadSharedLibrary(final Context context, final String libName) { - Throwable noAbiException; - try { - System.loadLibrary(libName); - return; - } catch (final UnsatisfiedLinkError e) { - Log.d(TAG, "Failed to load library normally, so attempting to extract from apk", e); - noAbiException = e; - } - File f = null; - try { - f = File.createTempFile("lib", ".so", context.getCodeCacheDir()); - if (extractLibrary(context, libName, f)) { - System.load(f.getAbsolutePath()); - return; - } - } catch (final Exception e) { - Log.d(TAG, "Failed to load library apk:/" + libName, e); - noAbiException = e; - } finally { - if (f != null) - // noinspection ResultOfMethodCallIgnored - f.delete(); - } - if (noAbiException instanceof RuntimeException) - throw (RuntimeException) noAbiException; - throw new RuntimeException(noAbiException); - } -} diff --git a/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java b/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java deleted file mode 100644 index ac18cabf..00000000 --- a/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.android.util; - -import android.content.Context; -import androidx.annotation.Nullable; -import android.system.OsConstants; -import android.util.Log; - -import com.wireguard.android.util.RootShell.RootShellException; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - -/** - * Helper to install WireGuard tools to the system partition. - */ - -public final class ToolsInstaller { - public static final int ERROR = 0x0; - public static final int MAGISK = 0x4; - 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 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.class.getSimpleName(); - - private final Context context; - private final RootShell rootShell; - private final File localBinaryDir; - private final Object lock = new Object(); - @Nullable private Boolean areToolsAvailable; - @Nullable private Boolean installAsMagiskModule; - - public ToolsInstaller(final Context context, final RootShell rootShell) { - localBinaryDir = new File(context.getCodeCacheDir(), "bin"); - this.context = context; - this.rootShell = rootShell; - } - - @Nullable - private static File getInstallDir() { - final String path = System.getenv("PATH"); - if (path == null) - return INSTALL_DIRS[0]; - final List<String> paths = Arrays.asList(path.split(":")); - for (final File dir : INSTALL_DIRS) { - if (paths.contains(dir.getPath()) && dir.isDirectory()) - return dir; - } - return null; - } - - public int areInstalled() throws RootShellException { - if (INSTALL_DIR == null) - return ERROR; - final StringBuilder script = new StringBuilder(); - for (final String name : EXECUTABLES) { - script.append(String.format("cmp -s '%s' '%s' && ", - new File(localBinaryDir, name).getAbsolutePath(), - new File(INSTALL_DIR, name).getAbsolutePath())); - } - script.append("exit ").append(OsConstants.EALREADY).append(';'); - try { - final int ret = rootShell.run(null, script.toString()); - if (ret == OsConstants.EALREADY) - return willInstallAsMagiskModule() ? YES | MAGISK : YES | SYSTEM; - else - return willInstallAsMagiskModule() ? NO | MAGISK : NO | SYSTEM; - } catch (final IOException ignored) { - return ERROR; - } catch (final RootShellException e) { - if (e.isIORelated()) - return ERROR; - throw e; - } - } - - public void ensureToolsAvailable() throws FileNotFoundException { - synchronized (lock) { - if (areToolsAvailable == null) { - try { - Log.d(TAG, extract() ? "Tools are now extracted into our private binary dir" : - "Tools were already extracted into our private binary dir"); - areToolsAvailable = true; - } catch (final IOException e) { - Log.e(TAG, "The wg and wg-quick tools are not available", e); - areToolsAvailable = false; - } - } - if (!areToolsAvailable) - throw new FileNotFoundException("Required tools unavailable"); - } - } - - 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."); - return willInstallAsMagiskModule() ? installMagisk() : installSystem(); - } - - private int installMagisk() throws RootShellException, IOException { - extract(); - final StringBuilder script = new StringBuilder("set -ex; "); - - script.append("trap 'rm -rf /sbin/.magisk/img/wireguard' INT TERM EXIT; "); - script.append(String.format("rm -rf /sbin/.magisk/img/wireguard/; mkdir -p /sbin/.magisk/img/wireguard%s; ", INSTALL_DIR)); - script.append("printf 'name=WireGuard Command Line Tools\nversion=1.0\nversionCode=1\nauthor=zx2c4\ndescription=Command line tools for WireGuard\nminMagisk=1500\n' > /sbin/.magisk/img/wireguard/module.prop; "); - script.append("touch /sbin/.magisk/img/wireguard/auto_mount; "); - for (final String name : EXECUTABLES) { - final File destination = new File("/sbin/.magisk/img/wireguard" + INSTALL_DIR, name); - script.append(String.format("cp '%s' '%s'; chmod 755 '%s'; chcon 'u:object_r:system_file:s0' '%s' || true; ", - new File(localBinaryDir, name), destination, destination, destination)); - } - script.append("trap - INT TERM EXIT;"); - - try { - return rootShell.run(null, script.toString()) == 0 ? YES | MAGISK : ERROR; - } catch (final IOException ignored) { - return ERROR; - } catch (final RootShellException e) { - if (e.isIORelated()) - return ERROR; - throw e; - } - } - - private int installSystem() throws RootShellException, IOException { - if (INSTALL_DIR == null) - return OsConstants.ENOENT; - extract(); - final StringBuilder script = new StringBuilder("set -ex; "); - script.append("trap 'mount -o ro,remount /system' EXIT; mount -o rw,remount /system; "); - for (final String name : EXECUTABLES) { - final File destination = new File(INSTALL_DIR, name); - script.append(String.format("cp '%s' '%s'; chmod 755 '%s'; restorecon '%s' || true; ", - new File(localBinaryDir, name), destination, destination, destination)); - } - try { - return rootShell.run(null, script.toString()) == 0 ? YES | SYSTEM : ERROR; - } catch (final IOException ignored) { - return ERROR; - } catch (final RootShellException e) { - if (e.isIORelated()) - return ERROR; - throw e; - } - } - - public boolean extract() throws IOException { - localBinaryDir.mkdirs(); - final File files[] = new File[EXECUTABLES.length]; - final File tempFiles[] = new File[EXECUTABLES.length]; - boolean allExist = true; - for (int i = 0; i < files.length; ++i) { - files[i] = new File(localBinaryDir, EXECUTABLES[i]); - tempFiles[i] = new File(localBinaryDir, EXECUTABLES[i] + ".tmp"); - allExist &= files[i].exists(); - } - if (allExist) - return false; - for (int i = 0; i < files.length; ++i) { - if (!SharedLibraryLoader.extractLibrary(context, EXECUTABLES[i], tempFiles[i])) - throw new FileNotFoundException("Unable to find " + EXECUTABLES[i]); - if (!tempFiles[i].setExecutable(true, false)) - throw new IOException("Unable to mark " + tempFiles[i].getAbsolutePath() + " as executable"); - if (!tempFiles[i].renameTo(files[i])) - throw new IOException("Unable to rename " + tempFiles[i].getAbsolutePath() + " to " + files[i].getAbsolutePath()); - } - return true; - } - - private boolean willInstallAsMagiskModule() { - synchronized (lock) { - if (installAsMagiskModule == null) { - try { - installAsMagiskModule = rootShell.run(null, "[ -d /sbin/.magisk/mirror -a -d /sbin/.magisk/img -a ! -f /cache/.disable_magisk ]") == OsConstants.EXIT_SUCCESS; - } catch (final Exception ignored) { - installAsMagiskModule = false; - } - } - return installAsMagiskModule; - } - } -} diff --git a/app/src/main/java/com/wireguard/config/Attribute.java b/app/src/main/java/com/wireguard/config/Attribute.java deleted file mode 100644 index 1e9e25f0..00000000 --- a/app/src/main/java/com/wireguard/config/Attribute.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright © 2018-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.config; - -import java.util.Iterator; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import java9.util.Optional; - -public final class Attribute { - private static final Pattern LINE_PATTERN = Pattern.compile("(\\w+)\\s*=\\s*([^\\s#][^#]*)"); - private static final Pattern LIST_SEPARATOR = Pattern.compile("\\s*,\\s*"); - - private final String key; - private final String value; - - private Attribute(final String key, final String value) { - this.key = key; - this.value = value; - } - - public static String join(final Iterable<?> values) { - final Iterator<?> it = values.iterator(); - if (!it.hasNext()) { - return ""; - } - final StringBuilder sb = new StringBuilder(); - sb.append(it.next()); - while (it.hasNext()) { - sb.append(", "); - sb.append(it.next()); - } - return sb.toString(); - } - - public static Optional<Attribute> parse(final CharSequence line) { - final Matcher matcher = LINE_PATTERN.matcher(line); - if (!matcher.matches()) - return Optional.empty(); - return Optional.of(new Attribute(matcher.group(1), matcher.group(2))); - } - - public static String[] split(final CharSequence value) { - return LIST_SEPARATOR.split(value); - } - - public String getKey() { - return key; - } - - public String getValue() { - return value; - } -} diff --git a/app/src/main/java/com/wireguard/config/BadConfigException.java b/app/src/main/java/com/wireguard/config/BadConfigException.java deleted file mode 100644 index 6d41b065..00000000 --- a/app/src/main/java/com/wireguard/config/BadConfigException.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright © 2018-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.config; - -import androidx.annotation.Nullable; - -import com.wireguard.crypto.KeyFormatException; - -public class BadConfigException extends Exception { - private final Location location; - private final Reason reason; - private final Section section; - @Nullable private final CharSequence text; - - private BadConfigException(final Section section, final Location location, - final Reason reason, @Nullable final CharSequence text, - @Nullable final Throwable cause) { - super(cause); - this.section = section; - this.location = location; - this.reason = reason; - this.text = text; - } - - public BadConfigException(final Section section, final Location location, - final Reason reason, @Nullable final CharSequence text) { - this(section, location, reason, text, null); - } - - public BadConfigException(final Section section, final Location location, - final KeyFormatException cause) { - this(section, location, Reason.INVALID_KEY, null, cause); - } - - public BadConfigException(final Section section, final Location location, - @Nullable final CharSequence text, - final NumberFormatException cause) { - this(section, location, Reason.INVALID_NUMBER, text, cause); - } - - public BadConfigException(final Section section, final Location location, - final ParseException cause) { - this(section, location, Reason.INVALID_VALUE, cause.getText(), cause); - } - - public Location getLocation() { - return location; - } - - public Reason getReason() { - return reason; - } - - public Section getSection() { - return section; - } - - @Nullable - public CharSequence getText() { - return text; - } - - public enum Location { - TOP_LEVEL(""), - ADDRESS("Address"), - ALLOWED_IPS("AllowedIPs"), - DNS("DNS"), - ENDPOINT("Endpoint"), - EXCLUDED_APPLICATIONS("ExcludedApplications"), - LISTEN_PORT("ListenPort"), - MTU("MTU"), - PERSISTENT_KEEPALIVE("PersistentKeepalive"), - PRE_SHARED_KEY("PresharedKey"), - PRIVATE_KEY("PrivateKey"), - PUBLIC_KEY("PublicKey"); - - private final String name; - - Location(final String name) { - this.name = name; - } - - public String getName() { - return name; - } - } - - public enum Reason { - INVALID_KEY, - INVALID_NUMBER, - INVALID_VALUE, - MISSING_ATTRIBUTE, - MISSING_SECTION, - MISSING_VALUE, - SYNTAX_ERROR, - UNKNOWN_ATTRIBUTE, - UNKNOWN_SECTION - } - - public enum Section { - CONFIG("Config"), - INTERFACE("Interface"), - PEER("Peer"); - - private final String name; - - Section(final String name) { - this.name = name; - } - - public String getName() { - return name; - } - } -} diff --git a/app/src/main/java/com/wireguard/config/Config.java b/app/src/main/java/com/wireguard/config/Config.java deleted file mode 100644 index 62651b08..00000000 --- a/app/src/main/java/com/wireguard/config/Config.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.config; - -import androidx.annotation.Nullable; - -import com.wireguard.config.BadConfigException.Location; -import com.wireguard.config.BadConfigException.Reason; -import com.wireguard.config.BadConfigException.Section; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -/** - * Represents the contents of a wg-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. - */ -public final class Config { - private final Interface interfaze; - private final List<Peer> peers; - - private Config(final Builder builder) { - interfaze = Objects.requireNonNull(builder.interfaze, "An [Interface] section is required"); - // Defensively copy to ensure immutability even if the Builder is reused. - peers = Collections.unmodifiableList(new ArrayList<>(builder.peers)); - } - - /** - * Parses an series of "Interface" and "Peer" sections into a {@code Config}. Throws - * {@link 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 - * @return a {@code Config} instance representing the supplied configuration - */ - public static Config parse(final InputStream stream) - throws IOException, BadConfigException { - return parse(new BufferedReader(new InputStreamReader(stream))); - } - - /** - * Parses an series of "Interface" and "Peer" sections into a {@code Config}. Throws - * {@link 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 - * @return a {@code Config} instance representing the supplied configuration - */ - public static Config parse(final BufferedReader reader) - throws IOException, BadConfigException { - final Builder builder = new Builder(); - final Collection<String> interfaceLines = new ArrayList<>(); - final Collection<String> peerLines = new ArrayList<>(); - boolean inInterfaceSection = false; - boolean inPeerSection = false; - @Nullable String line; - while ((line = reader.readLine()) != null) { - final int commentIndex = line.indexOf('#'); - if (commentIndex != -1) - line = line.substring(0, commentIndex); - line = line.trim(); - if (line.isEmpty()) - continue; - if (line.startsWith("[")) { - // Consume all [Peer] lines read so far. - if (inPeerSection) { - builder.parsePeer(peerLines); - peerLines.clear(); - } - if ("[Interface]".equalsIgnoreCase(line)) { - inInterfaceSection = true; - inPeerSection = false; - } else if ("[Peer]".equalsIgnoreCase(line)) { - inInterfaceSection = false; - inPeerSection = true; - } else { - throw new BadConfigException(Section.CONFIG, Location.TOP_LEVEL, - Reason.UNKNOWN_SECTION, line); - } - } else if (inInterfaceSection) { - interfaceLines.add(line); - } else if (inPeerSection) { - peerLines.add(line); - } else { - throw new BadConfigException(Section.CONFIG, Location.TOP_LEVEL, - Reason.UNKNOWN_SECTION, line); - } - } - if (inPeerSection) - builder.parsePeer(peerLines); - else if (!inInterfaceSection) - throw new BadConfigException(Section.CONFIG, Location.TOP_LEVEL, - Reason.MISSING_SECTION, null); - // Combine all [Interface] sections in the file. - builder.parseInterface(interfaceLines); - return builder.build(); - } - - @Override - public boolean equals(final Object obj) { - if (!(obj instanceof Config)) - return false; - final Config other = (Config) obj; - return interfaze.equals(other.interfaze) && peers.equals(other.peers); - } - - /** - * Returns the interface section of the configuration. - * - * @return the interface configuration - */ - public Interface getInterface() { - return interfaze; - } - - /** - * Returns a list of the configuration's peer sections. - * - * @return a list of {@link Peer}s - */ - public List<Peer> getPeers() { - return peers; - } - - @Override - public int hashCode() { - return 31 * interfaze.hashCode() + peers.hashCode(); - } - - /** - * Converts the {@code Config} into a string suitable for debugging purposes. The {@code Config} - * is identified by its interface's public key and the number of peers it has. - * - * @return a concise single-line identifier for the {@code Config} - */ - @Override - public String toString() { - return "(Config " + interfaze + " (" + peers.size() + " peers))"; - } - - /** - * Converts the {@code Config} into a string suitable for use as a {@code wg-quick} - * configuration file. - * - * @return the {@code Config} represented as one [Interface] and zero or more [Peer] sections - */ - public String toWgQuickString() { - final StringBuilder sb = new StringBuilder(); - sb.append("[Interface]\n").append(interfaze.toWgQuickString()); - for (final Peer peer : peers) - sb.append("\n[Peer]\n").append(peer.toWgQuickString()); - return sb.toString(); - } - - /** - * Serializes the {@code Config} for use with the WireGuard cross-platform userspace API. - * - * @return the {@code Config} represented as a series of "key=value" lines - */ - public String toWgUserspaceString() { - final StringBuilder sb = new StringBuilder(); - sb.append(interfaze.toWgUserspaceString()); - sb.append("replace_peers=true\n"); - for (final Peer peer : peers) - sb.append(peer.toWgUserspaceString()); - return sb.toString(); - } - - @SuppressWarnings("UnusedReturnValue") - public static final class Builder { - // Defaults to an empty set. - private final Set<Peer> peers = new LinkedHashSet<>(); - // No default; must be provided before building. - @Nullable private Interface interfaze; - - public Builder addPeer(final Peer peer) { - peers.add(peer); - return this; - } - - public Builder addPeers(final Collection<Peer> peers) { - this.peers.addAll(peers); - return this; - } - - public Config build() { - if (interfaze == null) - throw new IllegalArgumentException("An [Interface] section is required"); - return new Config(this); - } - - public Builder parseInterface(final Iterable<? extends CharSequence> lines) - throws BadConfigException { - return setInterface(Interface.parse(lines)); - } - - public Builder parsePeer(final Iterable<? extends CharSequence> lines) - throws BadConfigException { - return addPeer(Peer.parse(lines)); - } - - public Builder setInterface(final Interface interfaze) { - this.interfaze = interfaze; - return this; - } - } -} diff --git a/app/src/main/java/com/wireguard/config/InetAddresses.java b/app/src/main/java/com/wireguard/config/InetAddresses.java deleted file mode 100644 index 5303e27f..00000000 --- a/app/src/main/java/com/wireguard/config/InetAddresses.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.config; - -import java.lang.reflect.Method; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.regex.Pattern; - -import javax.annotation.Nullable; - -/** - * Utility methods for creating instances of {@link InetAddress}. - */ -public final class InetAddresses { - @Nullable private static final Method PARSER_METHOD; - private static final Pattern WONT_TOUCH_RESOLVER = Pattern.compile("^(((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?)|((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))$"); - - static { - Method m = null; - try { - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.Q) - // noinspection JavaReflectionMemberAccess - m = InetAddress.class.getMethod("parseNumericAddress", String.class); - } catch (final Exception ignored) { - } - PARSER_METHOD = m; - } - - private InetAddresses() { } - - /** - * Parses a numeric IPv4 or IPv6 address without performing any DNS lookups. - * - * @param address a string representing the IP address - * @return an instance of {@link Inet4Address} or {@link Inet6Address}, as appropriate - */ - public static InetAddress parse(final String address) throws ParseException { - if (address.isEmpty()) - throw new ParseException(InetAddress.class, address, "Empty address"); - try { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) - return android.net.InetAddresses.parseNumericAddress(address); - else if (PARSER_METHOD != null) - return (InetAddress) PARSER_METHOD.invoke(null, address); - else - throw new NoSuchMethodException("parseNumericAddress"); - } catch (final IllegalArgumentException e) { - throw new ParseException(InetAddress.class, address, e); - } catch (final Exception e) { - final Throwable cause = e.getCause(); - // Re-throw parsing exceptions with the original type, as callers might try to catch - // them. On the other hand, callers cannot be expected to handle reflection failures. - if (cause instanceof IllegalArgumentException) - throw new ParseException(InetAddress.class, address, cause); - try { - if (WONT_TOUCH_RESOLVER.matcher(address).matches()) - return InetAddress.getByName(address); - else - throw new ParseException(InetAddress.class, address, "Not an IP address"); - } catch (final UnknownHostException f) { - throw new ParseException(InetAddress.class, address, f); - } - } - } -} diff --git a/app/src/main/java/com/wireguard/config/InetEndpoint.java b/app/src/main/java/com/wireguard/config/InetEndpoint.java deleted file mode 100644 index a442258e..00000000 --- a/app/src/main/java/com/wireguard/config/InetEndpoint.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.config; - -import androidx.annotation.Nullable; - -import org.threeten.bp.Duration; -import org.threeten.bp.Instant; - -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.UnknownHostException; -import java.util.regex.Pattern; - -import java9.util.Optional; - - -/** - * An external endpoint (host and port) used to connect to a WireGuard {@link Peer}. - * <p> - * Instances of this class are externally immutable. - */ -public final class InetEndpoint { - private static final Pattern BARE_IPV6 = Pattern.compile("^[^\\[\\]]*:[^\\[\\]]*"); - private static final Pattern FORBIDDEN_CHARACTERS = Pattern.compile("[/?#]"); - - private final String host; - private final boolean isResolved; - private final Object lock = new Object(); - private final int port; - private Instant lastResolution = Instant.EPOCH; - @Nullable private InetEndpoint resolved; - - private InetEndpoint(final String host, final boolean isResolved, final int port) { - this.host = host; - this.isResolved = isResolved; - this.port = port; - } - - public static InetEndpoint parse(final String endpoint) throws ParseException { - if (FORBIDDEN_CHARACTERS.matcher(endpoint).find()) - throw new ParseException(InetEndpoint.class, endpoint, "Forbidden characters"); - final URI uri; - try { - uri = new URI("wg://" + endpoint); - } catch (final URISyntaxException e) { - throw new IllegalArgumentException(e); - } - if (uri.getPort() < 0 || uri.getPort() > 65535) - throw new ParseException(InetEndpoint.class, endpoint, "Missing/invalid port number"); - try { - InetAddresses.parse(uri.getHost()); - // Parsing ths host as a numeric address worked, so we don't need to do DNS lookups. - return new InetEndpoint(uri.getHost(), true, uri.getPort()); - } catch (final ParseException ignored) { - // Failed to parse the host as a numeric address, so it must be a DNS hostname/FQDN. - return new InetEndpoint(uri.getHost(), false, uri.getPort()); - } - } - - @Override - public boolean equals(final Object obj) { - if (!(obj instanceof InetEndpoint)) - return false; - final InetEndpoint other = (InetEndpoint) obj; - return host.equals(other.host) && port == other.port; - } - - public String getHost() { - return host; - } - - public int getPort() { - return port; - } - - /** - * Generate an {@code InetEndpoint} instance with the same port and the host resolved using DNS - * to a numeric address. If the host is already numeric, the existing instance may be returned. - * Because this function may perform network I/O, it must not be called from the main thread. - * - * @return the resolved endpoint, or {@link Optional#empty()} - */ - public Optional<InetEndpoint> getResolved() { - if (isResolved) - return Optional.of(this); - synchronized (lock) { - //TODO(zx2c4): Implement a real timeout mechanism using DNS TTL - if (Duration.between(lastResolution, Instant.now()).toMinutes() > 1) { - try { - // Prefer v4 endpoints over v6 to work around DNS64 and IPv6 NAT issues. - final InetAddress[] candidates = InetAddress.getAllByName(host); - InetAddress address = candidates[0]; - for (final InetAddress candidate : candidates) { - if (candidate instanceof Inet4Address) { - address = candidate; - break; - } - } - resolved = new InetEndpoint(address.getHostAddress(), true, port); - lastResolution = Instant.now(); - } catch (final UnknownHostException e) { - resolved = null; - } - } - return Optional.ofNullable(resolved); - } - } - - @Override - public int hashCode() { - return host.hashCode() ^ port; - } - - @Override - public String toString() { - final boolean isBareIpv6 = isResolved && BARE_IPV6.matcher(host).matches(); - return (isBareIpv6 ? '[' + host + ']' : host) + ':' + port; - } -} diff --git a/app/src/main/java/com/wireguard/config/InetNetwork.java b/app/src/main/java/com/wireguard/config/InetNetwork.java deleted file mode 100644 index f89322fd..00000000 --- a/app/src/main/java/com/wireguard/config/InetNetwork.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.config; - -import java.net.Inet4Address; -import java.net.InetAddress; - -/** - * An Internet network, denoted by its address and netmask - * <p> - * Instances of this class are immutable. - */ -public final class InetNetwork { - private final InetAddress address; - private final int mask; - - private InetNetwork(final InetAddress address, final int mask) { - this.address = address; - this.mask = mask; - } - - public static InetNetwork parse(final String network) throws ParseException { - final int slash = network.lastIndexOf('/'); - final String maskString; - final int rawMask; - final String rawAddress; - if (slash >= 0) { - maskString = network.substring(slash + 1); - try { - rawMask = Integer.parseInt(maskString, 10); - } catch (final NumberFormatException ignored) { - throw new ParseException(Integer.class, maskString); - } - rawAddress = network.substring(0, slash); - } else { - maskString = ""; - rawMask = -1; - rawAddress = network; - } - final InetAddress address = InetAddresses.parse(rawAddress); - final int maxMask = (address instanceof Inet4Address) ? 32 : 128; - if (rawMask > maxMask) - throw new ParseException(InetNetwork.class, maskString, "Invalid network mask"); - final int mask = rawMask >= 0 && rawMask <= maxMask ? rawMask : maxMask; - return new InetNetwork(address, mask); - } - - @Override - public boolean equals(final Object obj) { - if (!(obj instanceof InetNetwork)) - return false; - final InetNetwork other = (InetNetwork) obj; - return address.equals(other.address) && mask == other.mask; - } - - public InetAddress getAddress() { - return address; - } - - public int getMask() { - return mask; - } - - @Override - public int hashCode() { - return address.hashCode() ^ mask; - } - - @Override - public String toString() { - return address.getHostAddress() + '/' + mask; - } -} diff --git a/app/src/main/java/com/wireguard/config/Interface.java b/app/src/main/java/com/wireguard/config/Interface.java deleted file mode 100644 index 54944424..00000000 --- a/app/src/main/java/com/wireguard/config/Interface.java +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.config; - -import androidx.annotation.Nullable; - -import com.wireguard.config.BadConfigException.Location; -import com.wireguard.config.BadConfigException.Reason; -import com.wireguard.config.BadConfigException.Section; -import com.wireguard.crypto.Key; -import com.wireguard.crypto.KeyFormatException; -import com.wireguard.crypto.KeyPair; - -import java.net.InetAddress; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.Set; - -import java9.util.Lists; -import java9.util.Optional; -import java9.util.stream.Collectors; -import java9.util.stream.StreamSupport; - -/** - * 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. - */ -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> excludedApplications; - 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)); - excludedApplications = Collections.unmodifiableSet(new LinkedHashSet<>(builder.excludedApplications)); - 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 "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) - && excludedApplications.equals(other.excludedApplications) - && 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 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 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 + 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 = StreamSupport.stream(dnsServers) - .map(InetAddress::getHostAddress) - .collect(Collectors.toUnmodifiableList()); - sb.append("DNS = ").append(Attribute.join(dnsServerStrings)).append('\n'); - } - if (!excludedApplications.isEmpty()) - sb.append("ExcludedApplications = ").append(Attribute.join(excludedApplications)).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> excludedApplications = 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 Interface build() throws BadConfigException { - if (keyPair == null) - throw new BadConfigException(Section.INTERFACE, Location.PRIVATE_KEY, - Reason.MISSING_ATTRIBUTE, 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 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)) - addDnsServer(InetAddresses.parse(dnsServer)); - return this; - } catch (final ParseException e) { - throw new BadConfigException(Section.INTERFACE, Location.DNS, e); - } - } - - public Builder parseExcludedApplications(final CharSequence apps) { - return excludeApplications(Lists.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; - } - } -} diff --git a/app/src/main/java/com/wireguard/config/ParseException.java b/app/src/main/java/com/wireguard/config/ParseException.java deleted file mode 100644 index c79d1fa1..00000000 --- a/app/src/main/java/com/wireguard/config/ParseException.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © 2018-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.config; - -import androidx.annotation.Nullable; - -/** - */ -public class ParseException extends Exception { - private final Class<?> parsingClass; - private final CharSequence text; - - public ParseException(final Class<?> parsingClass, final CharSequence text, - @Nullable final String message, @Nullable final Throwable cause) { - super(message, cause); - this.parsingClass = parsingClass; - this.text = text; - } - - public ParseException(final Class<?> parsingClass, final CharSequence text, - @Nullable final String message) { - this(parsingClass, text, message, null); - } - - public ParseException(final Class<?> parsingClass, final CharSequence text, - @Nullable final Throwable cause) { - this(parsingClass, text, null, cause); - } - - public ParseException(final Class<?> parsingClass, final CharSequence text) { - this(parsingClass, text, null, null); - } - - public Class<?> getParsingClass() { - return parsingClass; - } - - public CharSequence getText() { - return text; - } -} diff --git a/app/src/main/java/com/wireguard/config/Peer.java b/app/src/main/java/com/wireguard/config/Peer.java deleted file mode 100644 index 37fcfa69..00000000 --- a/app/src/main/java/com/wireguard/config/Peer.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.config; - -import androidx.annotation.Nullable; - -import com.wireguard.config.BadConfigException.Location; -import com.wireguard.config.BadConfigException.Reason; -import com.wireguard.config.BadConfigException.Section; -import com.wireguard.crypto.Key; -import com.wireguard.crypto.KeyFormatException; - -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Locale; -import java.util.Objects; -import java.util.Set; - -import java9.util.Optional; - -/** - * Represents the configuration for a WireGuard peer (a [Peer] block). Peers must have a public key, - * and may optionally have several other attributes. - * <p> - * Instances of this class are immutable. - */ -public final class Peer { - private final Set<InetNetwork> allowedIps; - private final Optional<InetEndpoint> endpoint; - private final Optional<Integer> persistentKeepalive; - private final Optional<Key> preSharedKey; - private final Key publicKey; - - private Peer(final Builder builder) { - // Defensively copy to ensure immutability even if the Builder is reused. - allowedIps = Collections.unmodifiableSet(new LinkedHashSet<>(builder.allowedIps)); - endpoint = builder.endpoint; - persistentKeepalive = builder.persistentKeepalive; - preSharedKey = builder.preSharedKey; - publicKey = Objects.requireNonNull(builder.publicKey, "Peers must have a public key"); - } - - /** - * Parses an series of "KEY = VALUE" lines into a {@code Peer}. 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 public key attribute - * @return a {@code Peer} with all of its attributes set from {@code lines} - */ - public static Peer parse(final Iterable<? extends CharSequence> lines) - throws BadConfigException { - final Builder builder = new Builder(); - for (final CharSequence line : lines) { - final Attribute attribute = Attribute.parse(line).orElseThrow(() -> - new BadConfigException(Section.PEER, Location.TOP_LEVEL, - Reason.SYNTAX_ERROR, line)); - switch (attribute.getKey().toLowerCase(Locale.ENGLISH)) { - case "allowedips": - builder.parseAllowedIPs(attribute.getValue()); - break; - case "endpoint": - builder.parseEndpoint(attribute.getValue()); - break; - case "persistentkeepalive": - builder.parsePersistentKeepalive(attribute.getValue()); - break; - case "presharedkey": - builder.parsePreSharedKey(attribute.getValue()); - break; - case "publickey": - builder.parsePublicKey(attribute.getValue()); - break; - default: - throw new BadConfigException(Section.PEER, Location.TOP_LEVEL, - Reason.UNKNOWN_ATTRIBUTE, attribute.getKey()); - } - } - return builder.build(); - } - - @Override - public boolean equals(final Object obj) { - if (!(obj instanceof Peer)) - return false; - final Peer other = (Peer) obj; - return allowedIps.equals(other.allowedIps) - && endpoint.equals(other.endpoint) - && persistentKeepalive.equals(other.persistentKeepalive) - && preSharedKey.equals(other.preSharedKey) - && publicKey.equals(other.publicKey); - } - - /** - * Returns the peer's set of allowed IPs. - * - * @return the set of allowed IPs - */ - public Set<InetNetwork> getAllowedIps() { - // The collection is already immutable. - return allowedIps; - } - - /** - * Returns the peer's endpoint. - * - * @return the endpoint, or {@code Optional.empty()} if none is configured - */ - public Optional<InetEndpoint> getEndpoint() { - return endpoint; - } - - /** - * Returns the peer's persistent keepalive. - * - * @return the persistent keepalive, or {@code Optional.empty()} if none is configured - */ - public Optional<Integer> getPersistentKeepalive() { - return persistentKeepalive; - } - - /** - * Returns the peer's pre-shared key. - * - * @return the pre-shared key, or {@code Optional.empty()} if none is configured - */ - public Optional<Key> getPreSharedKey() { - return preSharedKey; - } - - /** - * Returns the peer's public key. - * - * @return the public key - */ - public Key getPublicKey() { - return publicKey; - } - - @Override - public int hashCode() { - int hash = 1; - hash = 31 * hash + allowedIps.hashCode(); - hash = 31 * hash + endpoint.hashCode(); - hash = 31 * hash + persistentKeepalive.hashCode(); - hash = 31 * hash + preSharedKey.hashCode(); - hash = 31 * hash + publicKey.hashCode(); - return hash; - } - - /** - * Converts the {@code Peer} into a string suitable for debugging purposes. The {@code Peer} is - * identified by its public key and (if known) its endpoint. - * - * @return a concise single-line identifier for the {@code Peer} - */ - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("(Peer "); - sb.append(publicKey.toBase64()); - endpoint.ifPresent(ep -> sb.append(" @").append(ep)); - sb.append(')'); - return sb.toString(); - } - - /** - * Converts the {@code Peer} into a string suitable for inclusion in a {@code wg-quick} - * configuration file. - * - * @return the {@code Peer} represented as a series of "Key = Value" lines - */ - public String toWgQuickString() { - final StringBuilder sb = new StringBuilder(); - if (!allowedIps.isEmpty()) - sb.append("AllowedIPs = ").append(Attribute.join(allowedIps)).append('\n'); - endpoint.ifPresent(ep -> sb.append("Endpoint = ").append(ep).append('\n')); - persistentKeepalive.ifPresent(pk -> sb.append("PersistentKeepalive = ").append(pk).append('\n')); - preSharedKey.ifPresent(psk -> sb.append("PreSharedKey = ").append(psk.toBase64()).append('\n')); - sb.append("PublicKey = ").append(publicKey.toBase64()).append('\n'); - return sb.toString(); - } - - /** - * Serializes the {@code Peer} for use with the WireGuard 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() { - 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'); - for (final InetNetwork allowedIp : allowedIps) - sb.append("allowed_ip=").append(allowedIp).append('\n'); - endpoint.flatMap(InetEndpoint::getResolved).ifPresent(ep -> sb.append("endpoint=").append(ep).append('\n')); - persistentKeepalive.ifPresent(pk -> sb.append("persistent_keepalive_interval=").append(pk).append('\n')); - preSharedKey.ifPresent(psk -> sb.append("preshared_key=").append(psk.toHex()).append('\n')); - return sb.toString(); - } - - @SuppressWarnings("UnusedReturnValue") - public static final class Builder { - // See wg(8) - private static final int MAX_PERSISTENT_KEEPALIVE = 65535; - - // Defaults to an empty set. - private final Set<InetNetwork> allowedIps = new LinkedHashSet<>(); - // Defaults to not present. - private Optional<InetEndpoint> endpoint = Optional.empty(); - // Defaults to not present. - private Optional<Integer> persistentKeepalive = Optional.empty(); - // Defaults to not present. - private Optional<Key> preSharedKey = Optional.empty(); - // No default; must be provided before building. - @Nullable private Key publicKey; - - public Builder addAllowedIp(final InetNetwork allowedIp) { - allowedIps.add(allowedIp); - return this; - } - - public Builder addAllowedIps(final Collection<InetNetwork> allowedIps) { - this.allowedIps.addAll(allowedIps); - return this; - } - - public Peer build() throws BadConfigException { - if (publicKey == null) - throw new BadConfigException(Section.PEER, Location.PUBLIC_KEY, - Reason.MISSING_ATTRIBUTE, null); - return new Peer(this); - } - - public Builder parseAllowedIPs(final CharSequence allowedIps) throws BadConfigException { - try { - for (final String allowedIp : Attribute.split(allowedIps)) - addAllowedIp(InetNetwork.parse(allowedIp)); - return this; - } catch (final ParseException e) { - throw new BadConfigException(Section.PEER, Location.ALLOWED_IPS, e); - } - } - - public Builder parseEndpoint(final String endpoint) throws BadConfigException { - try { - return setEndpoint(InetEndpoint.parse(endpoint)); - } catch (final ParseException e) { - throw new BadConfigException(Section.PEER, Location.ENDPOINT, e); - } - } - - public Builder parsePersistentKeepalive(final String persistentKeepalive) - throws BadConfigException { - try { - return setPersistentKeepalive(Integer.parseInt(persistentKeepalive)); - } catch (final NumberFormatException e) { - throw new BadConfigException(Section.PEER, Location.PERSISTENT_KEEPALIVE, - persistentKeepalive, e); - } - } - - public Builder parsePreSharedKey(final String preSharedKey) throws BadConfigException { - try { - return setPreSharedKey(Key.fromBase64(preSharedKey)); - } catch (final KeyFormatException e) { - throw new BadConfigException(Section.PEER, Location.PRE_SHARED_KEY, e); - } - } - - public Builder parsePublicKey(final String publicKey) throws BadConfigException { - try { - return setPublicKey(Key.fromBase64(publicKey)); - } catch (final KeyFormatException e) { - throw new BadConfigException(Section.PEER, Location.PUBLIC_KEY, e); - } - } - - public Builder setEndpoint(final InetEndpoint endpoint) { - this.endpoint = Optional.of(endpoint); - return this; - } - - public Builder setPersistentKeepalive(final int persistentKeepalive) - throws BadConfigException { - if (persistentKeepalive < 0 || persistentKeepalive > MAX_PERSISTENT_KEEPALIVE) - throw new BadConfigException(Section.PEER, Location.PERSISTENT_KEEPALIVE, - Reason.INVALID_VALUE, String.valueOf(persistentKeepalive)); - this.persistentKeepalive = persistentKeepalive == 0 ? - Optional.empty() : Optional.of(persistentKeepalive); - return this; - } - - public Builder setPreSharedKey(final Key preSharedKey) { - this.preSharedKey = Optional.of(preSharedKey); - return this; - } - - public Builder setPublicKey(final Key publicKey) { - this.publicKey = publicKey; - return this; - } - } -} diff --git a/app/src/main/java/com/wireguard/crypto/Curve25519.java b/app/src/main/java/com/wireguard/crypto/Curve25519.java deleted file mode 100644 index 5622fc5f..00000000 --- a/app/src/main/java/com/wireguard/crypto/Curve25519.java +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Copyright © 2016 Southern Storm Software, Pty Ltd. - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.crypto; - -import androidx.annotation.Nullable; - -import java.util.Arrays; - -/** - * Implementation of the Curve25519 elliptic curve algorithm. - * <p> - * This implementation was imported to WireGuard from noise-java: - * https://github.com/rweather/noise-java - * <p> - * This implementation is based on that from arduinolibs: - * https://github.com/rweather/arduinolibs - * <p> - * Differences in this version are due to using 26-bit limbs for the - * representation instead of the 8/16/32-bit limbs in the original. - * <p> - * References: http://cr.yp.to/ecdh.html, RFC 7748 - */ -@SuppressWarnings({"MagicNumber", "NonConstantFieldWithUpperCaseName", "SuspiciousNameCombination"}) -public final class Curve25519 { - // Numbers modulo 2^255 - 19 are broken up into ten 26-bit words. - private static final int NUM_LIMBS_255BIT = 10; - private static final int NUM_LIMBS_510BIT = 20; - - private final int[] A; - private final int[] AA; - private final int[] B; - private final int[] BB; - private final int[] C; - private final int[] CB; - private final int[] D; - private final int[] DA; - private final int[] E; - private final long[] t1; - private final int[] t2; - private final int[] x_1; - private final int[] x_2; - private final int[] x_3; - private final int[] z_2; - private final int[] z_3; - - /** - * Constructs the temporary state holder for Curve25519 evaluation. - */ - private Curve25519() { - // Allocate memory for all of the temporary variables we will need. - x_1 = new int[NUM_LIMBS_255BIT]; - x_2 = new int[NUM_LIMBS_255BIT]; - x_3 = new int[NUM_LIMBS_255BIT]; - z_2 = new int[NUM_LIMBS_255BIT]; - z_3 = new int[NUM_LIMBS_255BIT]; - A = new int[NUM_LIMBS_255BIT]; - B = new int[NUM_LIMBS_255BIT]; - C = new int[NUM_LIMBS_255BIT]; - D = new int[NUM_LIMBS_255BIT]; - E = new int[NUM_LIMBS_255BIT]; - AA = new int[NUM_LIMBS_255BIT]; - BB = new int[NUM_LIMBS_255BIT]; - DA = new int[NUM_LIMBS_255BIT]; - CB = new int[NUM_LIMBS_255BIT]; - t1 = new long[NUM_LIMBS_510BIT]; - t2 = new int[NUM_LIMBS_510BIT]; - } - - /** - * Conditional swap of two values. - * - * @param select Set to 1 to swap, 0 to leave as-is. - * @param x The first value. - * @param y The second value. - */ - private static void cswap(int select, final int[] x, final int[] y) { - select = -select; - for (int index = 0; index < NUM_LIMBS_255BIT; ++index) { - final int dummy = select & (x[index] ^ y[index]); - x[index] ^= dummy; - y[index] ^= dummy; - } - } - - /** - * Evaluates the Curve25519 curve. - * - * @param result Buffer to place the result of the evaluation into. - * @param offset Offset into the result buffer. - * @param privateKey The private key to use in the evaluation. - * @param publicKey The public key to use in the evaluation, or null - * if the base point of the curve should be used. - */ - public static void eval(final byte[] result, final int offset, - final byte[] privateKey, @Nullable final byte[] publicKey) { - final Curve25519 state = new Curve25519(); - try { - // Unpack the public key value. If null, use 9 as the base point. - Arrays.fill(state.x_1, 0); - if (publicKey != null) { - // Convert the input value from little-endian into 26-bit limbs. - for (int index = 0; index < 32; ++index) { - final int bit = (index * 8) % 26; - final int word = (index * 8) / 26; - final int value = publicKey[index] & 0xFF; - if (bit <= (26 - 8)) { - state.x_1[word] |= value << bit; - } else { - state.x_1[word] |= value << bit; - state.x_1[word] &= 0x03FFFFFF; - state.x_1[word + 1] |= value >> (26 - bit); - } - } - - // Just in case, we reduce the number modulo 2^255 - 19 to - // make sure that it is in range of the field before we start. - // This eliminates values between 2^255 - 19 and 2^256 - 1. - state.reduceQuick(state.x_1); - state.reduceQuick(state.x_1); - } else { - state.x_1[0] = 9; - } - - // Initialize the other temporary variables. - Arrays.fill(state.x_2, 0); // x_2 = 1 - state.x_2[0] = 1; - Arrays.fill(state.z_2, 0); // z_2 = 0 - System.arraycopy(state.x_1, 0, state.x_3, 0, state.x_1.length); // x_3 = x_1 - Arrays.fill(state.z_3, 0); // z_3 = 1 - state.z_3[0] = 1; - - // Evaluate the curve for every bit of the private key. - state.evalCurve(privateKey); - - // Compute x_2 * (z_2 ^ (p - 2)) where p = 2^255 - 19. - state.recip(state.z_3, state.z_2); - state.mul(state.x_2, state.x_2, state.z_3); - - // Convert x_2 into little-endian in the result buffer. - for (int index = 0; index < 32; ++index) { - final int bit = (index * 8) % 26; - final int word = (index * 8) / 26; - if (bit <= (26 - 8)) - result[offset + index] = (byte) (state.x_2[word] >> bit); - else - result[offset + index] = (byte) ((state.x_2[word] >> bit) | (state.x_2[word + 1] << (26 - bit))); - } - } finally { - // Clean up all temporary state before we exit. - state.destroy(); - } - } - - /** - * Subtracts two numbers modulo 2^255 - 19. - * - * @param result The result. - * @param x The first number to subtract. - * @param y The second number to subtract. - */ - private static void sub(final int[] result, final int[] x, final int[] y) { - int index; - int borrow; - - // Subtract y from x to generate the intermediate result. - borrow = 0; - for (index = 0; index < NUM_LIMBS_255BIT; ++index) { - borrow = x[index] - y[index] - ((borrow >> 26) & 0x01); - result[index] = borrow & 0x03FFFFFF; - } - - // If we had a borrow, then the result has gone negative and we - // have to add 2^255 - 19 to the result to make it positive again. - // The top bits of "borrow" will be all 1's if there is a borrow - // or it will be all 0's if there was no borrow. Easiest is to - // conditionally subtract 19 and then mask off the high bits. - borrow = result[0] - ((-((borrow >> 26) & 0x01)) & 19); - result[0] = borrow & 0x03FFFFFF; - for (index = 1; index < NUM_LIMBS_255BIT; ++index) { - borrow = result[index] - ((borrow >> 26) & 0x01); - result[index] = borrow & 0x03FFFFFF; - } - result[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF; - } - - /** - * Adds two numbers modulo 2^255 - 19. - * - * @param result The result. - * @param x The first number to add. - * @param y The second number to add. - */ - private void add(final int[] result, final int[] x, final int[] y) { - int carry = x[0] + y[0]; - result[0] = carry & 0x03FFFFFF; - for (int index = 1; index < NUM_LIMBS_255BIT; ++index) { - carry = (carry >> 26) + x[index] + y[index]; - result[index] = carry & 0x03FFFFFF; - } - reduceQuick(result); - } - - /** - * Destroy all sensitive data in this object. - */ - private void destroy() { - // Destroy all temporary variables. - Arrays.fill(x_1, 0); - Arrays.fill(x_2, 0); - Arrays.fill(x_3, 0); - Arrays.fill(z_2, 0); - Arrays.fill(z_3, 0); - Arrays.fill(A, 0); - Arrays.fill(B, 0); - Arrays.fill(C, 0); - Arrays.fill(D, 0); - Arrays.fill(E, 0); - Arrays.fill(AA, 0); - Arrays.fill(BB, 0); - Arrays.fill(DA, 0); - Arrays.fill(CB, 0); - Arrays.fill(t1, 0L); - Arrays.fill(t2, 0); - } - - /** - * Evaluates the curve for every bit in a secret key. - * - * @param s The 32-byte secret key. - */ - private void evalCurve(final byte[] s) { - int sposn = 31; - int sbit = 6; - int svalue = s[sposn] | 0x40; - int swap = 0; - - // Iterate over all 255 bits of "s" from the highest to the lowest. - // We ignore the high bit of the 256-bit representation of "s". - while (true) { - // Conditional swaps on entry to this bit but only if we - // didn't swap on the previous bit. - final int select = (svalue >> sbit) & 0x01; - swap ^= select; - cswap(swap, x_2, x_3); - cswap(swap, z_2, z_3); - swap = select; - - // Evaluate the curve. - add(A, x_2, z_2); // A = x_2 + z_2 - square(AA, A); // AA = A^2 - sub(B, x_2, z_2); // B = x_2 - z_2 - square(BB, B); // BB = B^2 - sub(E, AA, BB); // E = AA - BB - add(C, x_3, z_3); // C = x_3 + z_3 - sub(D, x_3, z_3); // D = x_3 - z_3 - mul(DA, D, A); // DA = D * A - mul(CB, C, B); // CB = C * B - add(x_3, DA, CB); // x_3 = (DA + CB)^2 - square(x_3, x_3); - sub(z_3, DA, CB); // z_3 = x_1 * (DA - CB)^2 - square(z_3, z_3); - mul(z_3, z_3, x_1); - mul(x_2, AA, BB); // x_2 = AA * BB - mulA24(z_2, E); // z_2 = E * (AA + a24 * E) - add(z_2, z_2, AA); - mul(z_2, z_2, E); - - // Move onto the next lower bit of "s". - if (sbit > 0) { - --sbit; - } else if (sposn == 0) { - break; - } else if (sposn == 1) { - --sposn; - svalue = s[sposn] & 0xF8; - sbit = 7; - } else { - --sposn; - svalue = s[sposn]; - sbit = 7; - } - } - - // Final conditional swaps. - cswap(swap, x_2, x_3); - cswap(swap, z_2, z_3); - } - - /** - * Multiplies two numbers modulo 2^255 - 19. - * - * @param result The result. - * @param x The first number to multiply. - * @param y The second number to multiply. - */ - private void mul(final int[] result, final int[] x, final int[] y) { - // Multiply the two numbers to create the intermediate result. - long v = x[0]; - for (int i = 0; i < NUM_LIMBS_255BIT; ++i) { - t1[i] = v * y[i]; - } - for (int i = 1; i < NUM_LIMBS_255BIT; ++i) { - v = x[i]; - for (int j = 0; j < (NUM_LIMBS_255BIT - 1); ++j) { - t1[i + j] += v * y[j]; - } - t1[i + NUM_LIMBS_255BIT - 1] = v * y[NUM_LIMBS_255BIT - 1]; - } - - // Propagate carries and convert back into 26-bit words. - v = t1[0]; - t2[0] = ((int) v) & 0x03FFFFFF; - for (int i = 1; i < NUM_LIMBS_510BIT; ++i) { - v = (v >> 26) + t1[i]; - t2[i] = ((int) v) & 0x03FFFFFF; - } - - // Reduce the result modulo 2^255 - 19. - reduce(result, t2, NUM_LIMBS_255BIT); - } - - /** - * Multiplies a number by the a24 constant, modulo 2^255 - 19. - * - * @param result The result. - * @param x The number to multiply by a24. - */ - private void mulA24(final int[] result, final int[] x) { - final long a24 = 121665; - long carry = 0; - for (int index = 0; index < NUM_LIMBS_255BIT; ++index) { - carry += a24 * x[index]; - t2[index] = ((int) carry) & 0x03FFFFFF; - carry >>= 26; - } - t2[NUM_LIMBS_255BIT] = ((int) carry) & 0x03FFFFFF; - reduce(result, t2, 1); - } - - /** - * Raise x to the power of (2^250 - 1). - * - * @param result The result. Must not overlap with x. - * @param x The argument. - */ - private void pow250(final int[] result, final int[] x) { - // The big-endian hexadecimal expansion of (2^250 - 1) is: - // 03FFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF - // - // The naive implementation needs to do 2 multiplications per 1 bit and - // 1 multiplication per 0 bit. We can improve upon this by creating a - // pattern 0000000001 ... 0000000001. If we square and multiply the - // pattern by itself we can turn the pattern into the partial results - // 0000000011 ... 0000000011, 0000000111 ... 0000000111, etc. - // This averages out to about 1.1 multiplications per 1 bit instead of 2. - - // Build a pattern of 250 bits in length of repeated copies of 0000000001. - square(A, x); - for (int j = 0; j < 9; ++j) - square(A, A); - mul(result, A, x); - for (int i = 0; i < 23; ++i) { - for (int j = 0; j < 10; ++j) - square(A, A); - mul(result, result, A); - } - - // Multiply bit-shifted versions of the 0000000001 pattern into - // the result to "fill in" the gaps in the pattern. - square(A, result); - mul(result, result, A); - for (int j = 0; j < 8; ++j) { - square(A, A); - mul(result, result, A); - } - } - - /** - * Computes the reciprocal of a number modulo 2^255 - 19. - * - * @param result The result. Must not overlap with x. - * @param x The argument. - */ - private void recip(final int[] result, final int[] x) { - // The reciprocal is the same as x ^ (p - 2) where p = 2^255 - 19. - // The big-endian hexadecimal expansion of (p - 2) is: - // 7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFEB - // Start with the 250 upper bits of the expansion of (p - 2). - pow250(result, x); - - // Deal with the 5 lowest bits of (p - 2), 01011, from highest to lowest. - square(result, result); - square(result, result); - mul(result, result, x); - square(result, result); - square(result, result); - mul(result, result, x); - square(result, result); - mul(result, result, x); - } - - /** - * Reduce a number modulo 2^255 - 19. - * - * @param result The result. - * @param x The value to be reduced. This array will be - * modified during the reduction. - * @param size The number of limbs in the high order half of x. - */ - private void reduce(final int[] result, final int[] x, final int size) { - // Calculate (x mod 2^255) + ((x / 2^255) * 19) which will - // either produce the answer we want or it will produce a - // value of the form "answer + j * (2^255 - 19)". There are - // 5 left-over bits in the top-most limb of the bottom half. - int carry = 0; - int limb = x[NUM_LIMBS_255BIT - 1] >> 21; - x[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF; - for (int index = 0; index < size; ++index) { - limb += x[NUM_LIMBS_255BIT + index] << 5; - carry += (limb & 0x03FFFFFF) * 19 + x[index]; - x[index] = carry & 0x03FFFFFF; - limb >>= 26; - carry >>= 26; - } - if (size < NUM_LIMBS_255BIT) { - // The high order half of the number is short; e.g. for mulA24(). - // Propagate the carry through the rest of the low order part. - for (int index = size; index < NUM_LIMBS_255BIT; ++index) { - carry += x[index]; - x[index] = carry & 0x03FFFFFF; - carry >>= 26; - } - } - - // The "j" value may still be too large due to the final carry-out. - // We must repeat the reduction. If we already have the answer, - // then this won't do any harm but we must still do the calculation - // to preserve the overall timing. The "j" value will be between - // 0 and 19, which means that the carry we care about is in the - // top 5 bits of the highest limb of the bottom half. - carry = (x[NUM_LIMBS_255BIT - 1] >> 21) * 19; - x[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF; - for (int index = 0; index < NUM_LIMBS_255BIT; ++index) { - carry += x[index]; - result[index] = carry & 0x03FFFFFF; - carry >>= 26; - } - - // At this point "x" will either be the answer or it will be the - // answer plus (2^255 - 19). Perform a trial subtraction to - // complete the reduction process. - reduceQuick(result); - } - - /** - * Reduces a number modulo 2^255 - 19 where it is known that the - * number can be reduced with only 1 trial subtraction. - * - * @param x The number to reduce, and the result. - */ - private void reduceQuick(final int[] x) { - // Perform a trial subtraction of (2^255 - 19) from "x" which is - // equivalent to adding 19 and subtracting 2^255. We add 19 here; - // the subtraction of 2^255 occurs in the next step. - int carry = 19; - for (int index = 0; index < NUM_LIMBS_255BIT; ++index) { - carry += x[index]; - t2[index] = carry & 0x03FFFFFF; - carry >>= 26; - } - - // If there was a borrow, then the original "x" is the correct answer. - // If there was no borrow, then "t2" is the correct answer. Select the - // correct answer but do it in a way that instruction timing will not - // reveal which value was selected. Borrow will occur if bit 21 of - // "t2" is zero. Turn the bit into a selection mask. - final int mask = -((t2[NUM_LIMBS_255BIT - 1] >> 21) & 0x01); - final int nmask = ~mask; - t2[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF; - for (int index = 0; index < NUM_LIMBS_255BIT; ++index) - x[index] = (x[index] & nmask) | (t2[index] & mask); - } - - /** - * Squares a number modulo 2^255 - 19. - * - * @param result The result. - * @param x The number to square. - */ - private void square(final int[] result, final int[] x) { - mul(result, x, x); - } -} diff --git a/app/src/main/java/com/wireguard/crypto/Key.java b/app/src/main/java/com/wireguard/crypto/Key.java deleted file mode 100644 index 6648a5f3..00000000 --- a/app/src/main/java/com/wireguard/crypto/Key.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.crypto; - -import com.wireguard.crypto.KeyFormatException.Type; - -import java.security.MessageDigest; -import java.security.SecureRandom; -import java.util.Arrays; - -/** - * Represents a WireGuard 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. - */ -@SuppressWarnings("MagicNumber") -public final class Key { - private final byte[] key; - - /** - * Constructs an object encapsulating the supplied key. - * - * @param key an array of bytes containing a binary key. Callers of this constructor are - * responsible for ensuring that the array is of the correct length. - */ - private Key(final byte[] key) { - // Defensively copy to ensure immutability. - this.key = Arrays.copyOf(key, key.length); - } - - /** - * Decodes a single 4-character base64 chunk to an integer in constant time. - * - * @param src an array of at least 4 characters in base64 format - * @param srcOffset the offset of the beginning of the chunk in {@code src} - * @return the decoded 3-byte integer, or some arbitrary integer value if the input was not - * valid base64 - */ - private static int decodeBase64(final char[] src, final int srcOffset) { - int val = 0; - for (int i = 0; i < 4; ++i) { - final char c = src[i + srcOffset]; - val |= (-1 - + ((((('A' - 1) - c) & (c - ('Z' + 1))) >>> 8) & (c - 64)) - + ((((('a' - 1) - c) & (c - ('z' + 1))) >>> 8) & (c - 70)) - + ((((('0' - 1) - c) & (c - ('9' + 1))) >>> 8) & (c + 5)) - + ((((('+' - 1) - c) & (c - ('+' + 1))) >>> 8) & 63) - + ((((('/' - 1) - c) & (c - ('/' + 1))) >>> 8) & 64) - ) << (18 - 6 * i); - } - return val; - } - - /** - * Encodes a single 4-character base64 chunk from 3 consecutive bytes in constant time. - * - * @param src an array of at least 3 bytes - * @param srcOffset the offset of the beginning of the chunk in {@code src} - * @param dest an array of at least 4 characters - * @param destOffset the offset of the beginning of the chunk in {@code dest} - */ - private static void encodeBase64(final byte[] src, final int srcOffset, - final char[] dest, final int destOffset) { - final byte[] input = { - (byte) ((src[srcOffset] >>> 2) & 63), - (byte) ((src[srcOffset] << 4 | ((src[1 + srcOffset] & 0xff) >>> 4)) & 63), - (byte) ((src[1 + srcOffset] << 2 | ((src[2 + srcOffset] & 0xff) >>> 6)) & 63), - (byte) ((src[2 + srcOffset]) & 63), - }; - for (int i = 0; i < 4; ++i) { - dest[i + destOffset] = (char) (input[i] + 'A' - + (((25 - input[i]) >>> 8) & 6) - - (((51 - input[i]) >>> 8) & 75) - - (((61 - input[i]) >>> 8) & 15) - + (((62 - input[i]) >>> 8) & 3)); - } - } - - /** - * Decodes a WireGuard 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 - * @return the decoded key encapsulated in an immutable container - */ - public static Key fromBase64(final String str) throws KeyFormatException { - final char[] input = str.toCharArray(); - if (input.length != Format.BASE64.length || input[Format.BASE64.length - 1] != '=') - throw new KeyFormatException(Format.BASE64, Type.LENGTH); - final byte[] key = new byte[Format.BINARY.length]; - int i; - int ret = 0; - for (i = 0; i < key.length / 3; ++i) { - final int val = decodeBase64(input, i * 4); - ret |= val >>> 31; - key[i * 3] = (byte) ((val >>> 16) & 0xff); - key[i * 3 + 1] = (byte) ((val >>> 8) & 0xff); - key[i * 3 + 2] = (byte) (val & 0xff); - } - final char[] endSegment = { - input[i * 4], - input[i * 4 + 1], - input[i * 4 + 2], - 'A', - }; - final int val = decodeBase64(endSegment, 0); - ret |= (val >>> 31) | (val & 0xff); - key[i * 3] = (byte) ((val >>> 16) & 0xff); - key[i * 3 + 1] = (byte) ((val >>> 8) & 0xff); - - if (ret != 0) - throw new KeyFormatException(Format.BASE64, Type.CONTENTS); - return new Key(key); - } - - /** - * Wraps a WireGuard 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 - * @return the key encapsulated in an immutable container - */ - public static Key fromBytes(final byte[] bytes) throws KeyFormatException { - if (bytes.length != Format.BINARY.length) - throw new KeyFormatException(Format.BINARY, Type.LENGTH); - return new Key(bytes); - } - - /** - * Decodes a WireGuard 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 - * @return the decoded key encapsulated in an immutable container - */ - public static Key fromHex(final String str) throws KeyFormatException { - final char[] input = str.toCharArray(); - if (input.length != Format.HEX.length) - throw new KeyFormatException(Format.HEX, Type.LENGTH); - final byte[] key = new byte[Format.BINARY.length]; - int ret = 0; - for (int i = 0; i < key.length; ++i) { - int c; - int cNum; - int cNum0; - int cAlpha; - int cAlpha0; - int cVal; - final int cAcc; - - c = input[i * 2]; - cNum = c ^ 48; - cNum0 = ((cNum - 10) >>> 8) & 0xff; - cAlpha = (c & ~32) - 55; - cAlpha0 = (((cAlpha - 10) ^ (cAlpha - 16)) >>> 8) & 0xff; - ret |= ((cNum0 | cAlpha0) - 1) >>> 8; - cVal = (cNum0 & cNum) | (cAlpha0 & cAlpha); - cAcc = cVal * 16; - - c = input[i * 2 + 1]; - cNum = c ^ 48; - cNum0 = ((cNum - 10) >>> 8) & 0xff; - cAlpha = (c & ~32) - 55; - cAlpha0 = (((cAlpha - 10) ^ (cAlpha - 16)) >>> 8) & 0xff; - ret |= ((cNum0 | cAlpha0) - 1) >>> 8; - cVal = (cNum0 & cNum) | (cAlpha0 & cAlpha); - key[i] = (byte) (cAcc | cVal); - } - if (ret != 0) - throw new KeyFormatException(Format.HEX, Type.CONTENTS); - return new Key(key); - } - - /** - * Generates a private key using the system's {@link SecureRandom} number generator. - * - * @return a well-formed random private key - */ - static Key generatePrivateKey() { - final SecureRandom secureRandom = new SecureRandom(); - final byte[] privateKey = new byte[Format.BINARY.getLength()]; - secureRandom.nextBytes(privateKey); - privateKey[0] &= 248; - privateKey[31] &= 127; - privateKey[31] |= 64; - return new Key(privateKey); - } - - /** - * Generates a public key from an existing private key. - * - * @param privateKey a private key - * @return a well-formed public key that corresponds to the supplied private key - */ - static Key generatePublicKey(final Key privateKey) { - final byte[] publicKey = new byte[Format.BINARY.getLength()]; - Curve25519.eval(publicKey, 0, privateKey.getBytes(), null); - return new Key(publicKey); - } - - /** - * Returns the key as an array of bytes. - * - * @return an array of bytes containing the raw binary key - */ - public byte[] getBytes() { - // Defensively copy to ensure immutability. - return Arrays.copyOf(key, key.length); - } - - /** - * Encodes the key to base64. - * - * @return a string containing the encoded key - */ - public String toBase64() { - final char[] output = new char[Format.BASE64.length]; - int i; - for (i = 0; i < key.length / 3; ++i) - encodeBase64(key, i * 3, output, i * 4); - final byte[] endSegment = { - key[i * 3], - key[i * 3 + 1], - 0, - }; - encodeBase64(endSegment, 0, output, i * 4); - output[Format.BASE64.length - 1] = '='; - return new String(output); - } - - /** - * Encodes the key to hexadecimal ASCII characters. - * - * @return a string containing the encoded key - */ - public String toHex() { - final char[] output = new char[Format.HEX.length]; - for (int i = 0; i < key.length; ++i) { - output[i * 2] = (char) (87 + (key[i] >> 4 & 0xf) - + ((((key[i] >> 4 & 0xf) - 10) >> 8) & ~38)); - output[i * 2 + 1] = (char) (87 + (key[i] & 0xf) - + ((((key[i] & 0xf) - 10) >> 8) & ~38)); - } - return new String(output); - } - - @Override - public int hashCode() { - int ret = 0; - for (int i = 0; i < key.length / 4; ++i) - ret ^= (key[i * 4 + 0] >> 0) + (key[i * 4 + 1] >> 8) + (key[i * 4 + 2] >> 16) + (key[i * 4 + 3] >> 24); - return ret; - } - - @Override - public boolean equals(final Object obj) { - if (obj == this) - return true; - if (obj == null || obj.getClass() != getClass()) - return false; - final Key other = (Key) obj; - return MessageDigest.isEqual(key, other.key); - } - - /** - * The supported formats for encoding a WireGuard key. - */ - public enum Format { - BASE64(44), - BINARY(32), - HEX(64); - - private final int length; - - Format(final int length) { - this.length = length; - } - - public int getLength() { - return length; - } - } - -} diff --git a/app/src/main/java/com/wireguard/crypto/KeyFormatException.java b/app/src/main/java/com/wireguard/crypto/KeyFormatException.java deleted file mode 100644 index 5818b4d4..00000000 --- a/app/src/main/java/com/wireguard/crypto/KeyFormatException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright © 2018-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.crypto; - -/** - * An exception thrown when attempting to parse an invalid key (too short, too long, or byte - * data inappropriate for the format). The format being parsed can be accessed with the - * {@link #getFormat} method. - */ -public final class KeyFormatException extends Exception { - private final Key.Format format; - private final Type type; - - KeyFormatException(final Key.Format format, final Type type) { - this.format = format; - this.type = type; - } - - public Key.Format getFormat() { - return format; - } - - public Type getType() { - return type; - } - - public enum Type { - CONTENTS, - LENGTH - } -} diff --git a/app/src/main/java/com/wireguard/crypto/KeyPair.java b/app/src/main/java/com/wireguard/crypto/KeyPair.java deleted file mode 100644 index f8238e91..00000000 --- a/app/src/main/java/com/wireguard/crypto/KeyPair.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.crypto; - -/** - * Represents a Curve25519 key pair as used by WireGuard. - * <p> - * Instances of this class are immutable. - */ -public class KeyPair { - private final Key privateKey; - private final Key publicKey; - - /** - * Creates a key pair using a newly-generated private key. - */ - public KeyPair() { - this(Key.generatePrivateKey()); - } - - /** - * Creates a key pair using an existing private key. - * - * @param privateKey a private key, used to derive the public key - */ - public KeyPair(final Key privateKey) { - this.privateKey = privateKey; - publicKey = Key.generatePublicKey(privateKey); - } - - /** - * Returns the private key from the key pair. - * - * @return the private key - */ - public Key getPrivateKey() { - return privateKey; - } - - /** - * Returns the public key from the key pair. - * - * @return the public key - */ - public Key getPublicKey() { - return publicKey; - } -} diff --git a/app/src/main/java/com/wireguard/util/NonNullForAll.java b/app/src/main/java/com/wireguard/util/NonNullForAll.java deleted file mode 100644 index f179fa49..00000000 --- a/app/src/main/java/com/wireguard/util/NonNullForAll.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.util; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; - -/** - * This annotation can be applied to a package, class or method to indicate that all - * class fields and method parameters and return values in that element are nonnull - * by default unless overridden. - */ -@Documented -@Nonnull -@TypeQualifierDefault({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) -@Retention(RetentionPolicy.RUNTIME) -public @interface NonNullForAll { -} diff --git a/app/tools/CMakeLists.txt b/app/tools/CMakeLists.txt deleted file mode 100644 index 90e61a06..00000000 --- a/app/tools/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# -# Copyright © 2018-2019 WireGuard LLC. All Rights Reserved. - -cmake_minimum_required(VERSION 3.4.1) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") - -# Work around https://github.com/android-ndk/ndk/issues/602 -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold") - -add_executable(libwg-quick.so wireguard-tools/src/wg-quick/android.c ndk-compat/compat.c) -target_compile_options(libwg-quick.so PUBLIC -O3 -std=gnu11 -Wall -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DWG_PACKAGE_NAME=\"${ANDROID_PACKAGE_NAME}\") -target_link_libraries(libwg-quick.so -ldl) - -file(GLOB WG_SOURCES wireguard-tools/src/*.c ndk-compat/compat.c) -add_executable(libwg.so ${WG_SOURCES}) -target_include_directories(libwg.so PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/wireguard-tools/src/uapi/" "${CMAKE_CURRENT_SOURCE_DIR}/wireguard-tools/src/") -target_compile_options(libwg.so PUBLIC -O3 -std=gnu11 -D_GNU_SOURCE -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DHAVE_VISIBILITY_HIDDEN -DRUNSTATEDIR=\"/data/data/${ANDROID_PACKAGE_NAME}/cache\") - -add_custom_target(libwg-go.so WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libwg-go" COMMENT "Building wireguard-go" VERBATIM COMMAND make - ANDROID_ARCH_NAME=${ANDROID_ARCH_NAME} - ANDROID_C_COMPILER=${ANDROID_C_COMPILER} - ANDROID_TOOLCHAIN_ROOT=${ANDROID_TOOLCHAIN_ROOT} - ANDROID_LLVM_TRIPLE=${ANDROID_LLVM_TRIPLE} - ANDROID_SYSROOT=${ANDROID_SYSROOT} - ANDROID_PACKAGE_NAME=${ANDROID_PACKAGE_NAME} - CFLAGS=${CMAKE_C_FLAGS}\ -Wno-unused-command-line-argument - LDFLAGS=${CMAKE_SHARED_LINKER_FLAGS}\ -fuse-ld=gold - DESTDIR=${CMAKE_LIBRARY_OUTPUT_DIRECTORY} - BUILDDIR=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/../generated-src -) -# Hack to make it actually build as part of the default target -add_dependencies(libwg.so libwg-go.so) diff --git a/app/tools/libwg-go/.gitignore b/app/tools/libwg-go/.gitignore deleted file mode 100644 index d1638636..00000000 --- a/app/tools/libwg-go/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build/
\ No newline at end of file diff --git a/app/tools/libwg-go/Makefile b/app/tools/libwg-go/Makefile deleted file mode 100644 index 5c46df52..00000000 --- a/app/tools/libwg-go/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# -# Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - -BUILDDIR ?= $(CURDIR)/build -DESTDIR ?= $(CURDIR)/out - -NDK_GO_ARCH_MAP_x86 := 386 -NDK_GO_ARCH_MAP_x86_64 := amd64 -NDK_GO_ARCH_MAP_arm := arm -NDK_GO_ARCH_MAP_arm64 := arm64 -NDK_GO_ARCH_MAP_mips := mipsx -NDK_GO_ARCH_MAP_mips64 := mips64x - -CLANG_FLAGS := --target=$(ANDROID_LLVM_TRIPLE) --gcc-toolchain=$(ANDROID_TOOLCHAIN_ROOT) --sysroot=$(ANDROID_SYSROOT) -export CGO_CFLAGS := $(CLANG_FLAGS) $(CFLAGS) -export CGO_LDFLAGS := $(CLANG_FLAGS) $(LDFLAGS) -export CC := $(ANDROID_C_COMPILER) -export GOARCH := $(NDK_GO_ARCH_MAP_$(ANDROID_ARCH_NAME)) -export GOOS := android -export CGO_ENABLED := 1 - -DESIRED_GO_VERSION := 1.13.7 - -default: $(DESTDIR)/libwg-go.so - -$(BUILDDIR)/go-$(DESIRED_GO_VERSION)/.prepared: - mkdir -p "$(dir $@)" - curl "https://dl.google.com/go/go$(DESIRED_GO_VERSION).$(shell uname -s | tr '[:upper:]' '[:lower:]')-$(NDK_GO_ARCH_MAP_$(shell uname -m)).tar.gz" | tar -C "$(dir $@)" --strip-components=1 -xzf - - patch -p1 -f -N -r- -d "$(dir $@)" < goruntime-boottime-over-monotonic.diff - touch "$@" - -$(DESTDIR)/libwg-go.so: export PATH := $(BUILDDIR)/go-$(DESIRED_GO_VERSION)/bin/:$(PATH) -$(DESTDIR)/libwg-go.so: $(BUILDDIR)/go-$(DESIRED_GO_VERSION)/.prepared go.mod - go build -tags linux -ldflags="-X golang.zx2c4.com/wireguard/ipc.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard" -v -trimpath -o "$@" -buildmode c-shared diff --git a/app/tools/libwg-go/api-android.go b/app/tools/libwg-go/api-android.go deleted file mode 100644 index 7a393cae..00000000 --- a/app/tools/libwg-go/api-android.go +++ /dev/null @@ -1,193 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * - * Copyright (C) 2017-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. - */ - -package main - -// #cgo LDFLAGS: -llog -// #include <android/log.h> -import "C" - -import ( - "bufio" - "golang.org/x/sys/unix" - "golang.zx2c4.com/wireguard/device" - "golang.zx2c4.com/wireguard/ipc" - "golang.zx2c4.com/wireguard/tun" - "bytes" - "log" - "math" - "net" - "os" - "os/signal" - "runtime" - "strings" - "unsafe" -) - -type AndroidLogger struct { - level C.int - interfaceName string -} - -func (l AndroidLogger) Write(p []byte) (int, error) { - C.__android_log_write(l.level, C.CString("WireGuard/GoBackend/"+l.interfaceName), C.CString(string(p))) - return len(p), nil -} - -type TunnelHandle struct { - device *device.Device - uapi net.Listener -} - -var tunnelHandles map[int32]TunnelHandle - -func init() { - device.RoamingDisabled = true - tunnelHandles = make(map[int32]TunnelHandle) - signals := make(chan os.Signal) - signal.Notify(signals, unix.SIGUSR2) - go func() { - buf := make([]byte, os.Getpagesize()) - for { - select { - case <-signals: - n := runtime.Stack(buf, true) - buf[n] = 0 - C.__android_log_write(C.ANDROID_LOG_ERROR, C.CString("WireGuard/GoBackend/Stacktrace"), (*C.char)(unsafe.Pointer(&buf[0]))) - } - } - }() -} - -//export wgTurnOn -func wgTurnOn(ifnameRef string, tunFd int32, settings string) int32 { - interfaceName := string([]byte(ifnameRef)) - - logger := &device.Logger{ - Debug: log.New(&AndroidLogger{level: C.ANDROID_LOG_DEBUG, interfaceName: interfaceName}, "", 0), - Info: log.New(&AndroidLogger{level: C.ANDROID_LOG_INFO, interfaceName: interfaceName}, "", 0), - Error: log.New(&AndroidLogger{level: C.ANDROID_LOG_ERROR, interfaceName: interfaceName}, "", 0), - } - - logger.Debug.Println("Debug log enabled") - - tun, name, err := tun.CreateUnmonitoredTUNFromFD(int(tunFd)) - if err != nil { - unix.Close(int(tunFd)) - logger.Error.Println(err) - return -1 - } - - logger.Info.Println("Attaching to interface", name) - device := device.NewDevice(tun, logger) - - setError := device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings))) - if setError != nil { - unix.Close(int(tunFd)) - logger.Error.Println(setError) - return -1 - } - - var uapi net.Listener - - uapiFile, err := ipc.UAPIOpen(name) - if err != nil { - logger.Error.Println(err) - } else { - uapi, err = ipc.UAPIListen(name, uapiFile) - if err != nil { - uapiFile.Close() - logger.Error.Println(err) - } else { - go func() { - for { - conn, err := uapi.Accept() - if err != nil { - return - } - go device.IpcHandle(conn) - } - }() - } - } - - device.Up() - logger.Info.Println("Device started") - - var i int32 - for i = 0; i < math.MaxInt32; i++ { - if _, exists := tunnelHandles[i]; !exists { - break - } - } - if i == math.MaxInt32 { - unix.Close(int(tunFd)) - return -1 - } - tunnelHandles[i] = TunnelHandle{device: device, uapi: uapi} - return i -} - -//export wgTurnOff -func wgTurnOff(tunnelHandle int32) { - handle, ok := tunnelHandles[tunnelHandle] - if !ok { - return - } - delete(tunnelHandles, tunnelHandle) - if handle.uapi != nil { - handle.uapi.Close() - } - handle.device.Close() -} - -//export wgGetSocketV4 -func wgGetSocketV4(tunnelHandle int32) int32 { - handle, ok := tunnelHandles[tunnelHandle] - if !ok { - return -1 - } - fd, err := handle.device.PeekLookAtSocketFd4() - if err != nil { - return -1 - } - return int32(fd) -} - -//export wgGetSocketV6 -func wgGetSocketV6(tunnelHandle int32) int32 { - handle, ok := tunnelHandles[tunnelHandle] - if !ok { - return -1 - } - fd, err := handle.device.PeekLookAtSocketFd6() - if err != nil { - return -1 - } - return int32(fd) -} - -//export wgGetConfig -func wgGetConfig(tunnelHandle int32) *C.char { - handle, ok := tunnelHandles[tunnelHandle] - if !ok { - return nil - } - settings := new(bytes.Buffer) - writer := bufio.NewWriter(settings) - err := handle.device.IpcGetOperation(writer) - if err != nil { - return nil - } - writer.Flush() - return C.CString(settings.String()) -} - -//export wgVersion -func wgVersion() *C.char { - return C.CString(device.WireGuardGoVersion) -} - -func main() {} diff --git a/app/tools/libwg-go/go.mod b/app/tools/libwg-go/go.mod deleted file mode 100644 index a5bd709b..00000000 --- a/app/tools/libwg-go/go.mod +++ /dev/null @@ -1,10 +0,0 @@ -module golang.zx2c4.com/wireguard/android - -go 1.13 - -require ( - golang.org/x/crypto v0.0.0-20200117160349-530e935923ad // indirect - golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa // indirect - golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 - golang.zx2c4.com/wireguard v0.0.20200121 -) diff --git a/app/tools/libwg-go/go.sum b/app/tools/libwg-go/go.sum deleted file mode 100644 index 25d8f90c..00000000 --- a/app/tools/libwg-go/go.sum +++ /dev/null @@ -1,19 +0,0 @@ -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200117160349-530e935923ad h1:Jh8cai0fqIK+f6nG0UgPW5wFk8wmiMhM3AyciDBdtQg= -golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.zx2c4.com/wireguard v0.0.20200121 h1:vcswa5Q6f+sylDfjqyrVNNrjsFUUbPsgAQTBCAg/Qf8= -golang.zx2c4.com/wireguard v0.0.20200121/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= diff --git a/app/tools/libwg-go/goruntime-boottime-over-monotonic.diff b/app/tools/libwg-go/goruntime-boottime-over-monotonic.diff deleted file mode 100644 index 5fd02397..00000000 --- a/app/tools/libwg-go/goruntime-boottime-over-monotonic.diff +++ /dev/null @@ -1,161 +0,0 @@ -From b19623e7673a4d6743745382d5d38751b64e011d Mon Sep 17 00:00:00 2001 -From: "Jason A. Donenfeld" <Jason@zx2c4.com> -Date: Wed, 27 Feb 2019 05:05:44 +0100 -Subject: [PATCH] runtime: use CLOCK_BOOTTIME in nanotime on Linux - -This makes timers account for having expired while a computer was -asleep, which is quite common on mobile devices. Note that BOOTTIME is -identical to MONOTONIC, except that it takes into account time spent -in suspend. In Linux 4.17, the kernel will actually make MONOTONIC act -like BOOTTIME anyway, so this switch will additionally unify the -timer behavior across kernels. - -BOOTTIME was introduced into Linux 2.6.39-rc1 with 70a08cca1227d in -2011. - -Fixes #24595 - -Change-Id: I7b2a6ca0c5bc5fce57ec0eeafe7b68270b429321 ---- - src/runtime/sys_linux_386.s | 4 ++-- - src/runtime/sys_linux_amd64.s | 2 +- - src/runtime/sys_linux_arm.s | 4 ++-- - src/runtime/sys_linux_arm64.s | 4 ++-- - src/runtime/sys_linux_mips64x.s | 2 +- - src/runtime/sys_linux_mipsx.s | 2 +- - src/runtime/sys_linux_ppc64x.s | 2 +- - src/runtime/sys_linux_s390x.s | 2 +- - 8 files changed, 11 insertions(+), 11 deletions(-) - -diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s -index 72c43bd9da..daadfe32a9 100644 ---- a/src/runtime/sys_linux_386.s -+++ b/src/runtime/sys_linux_386.s -@@ -288,13 +288,13 @@ noswitch: - - LEAL 8(SP), BX // &ts (struct timespec) - MOVL BX, 4(SP) -- MOVL $1, 0(SP) // CLOCK_MONOTONIC -+ MOVL $7, 0(SP) // CLOCK_BOOTTIME - CALL AX - JMP finish - - fallback: - MOVL $SYS_clock_gettime, AX -- MOVL $1, BX // CLOCK_MONOTONIC -+ MOVL $7, BX // CLOCK_BOOTTIME - LEAL 8(SP), CX - INVOKE_SYSCALL - -diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s -index 5c300f553d..e4a6f12ec6 100644 ---- a/src/runtime/sys_linux_amd64.s -+++ b/src/runtime/sys_linux_amd64.s -@@ -261,7 +261,7 @@ noswitch: - MOVQ runtime·vdsoClockgettimeSym(SB), AX - CMPQ AX, $0 - JEQ fallback -- MOVL $1, DI // CLOCK_MONOTONIC -+ MOVL $7, DI // CLOCK_BOOTTIME - LEAQ 0(SP), SI - CALL AX - MOVQ 0(SP), AX // sec -diff --git a/src/runtime/sys_linux_arm.s b/src/runtime/sys_linux_arm.s -index 9c7398451c..61b6cd91f6 100644 ---- a/src/runtime/sys_linux_arm.s -+++ b/src/runtime/sys_linux_arm.s -@@ -11,7 +11,7 @@ - #include "textflag.h" - - #define CLOCK_REALTIME 0 --#define CLOCK_MONOTONIC 1 -+#define CLOCK_BOOTTIME 7 - - // for EABI, as we don't support OABI - #define SYS_BASE 0x0 -@@ -291,7 +291,7 @@ noswitch: - SUB $24, R13 // Space for results - BIC $0x7, R13 // Align for C code - -- MOVW $CLOCK_MONOTONIC, R0 -+ MOVW $CLOCK_BOOTTIME, R0 - MOVW $8(R13), R1 // timespec - MOVW runtime·vdsoClockgettimeSym(SB), R11 - CMP $0, R11 -diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s -index 2835b6ca1c..346ca9cfce 100644 ---- a/src/runtime/sys_linux_arm64.s -+++ b/src/runtime/sys_linux_arm64.s -@@ -13,7 +13,7 @@ - #define AT_FDCWD -100 - - #define CLOCK_REALTIME 0 --#define CLOCK_MONOTONIC 1 -+#define CLOCK_BOOTTIME 7 - - #define SYS_exit 93 - #define SYS_read 63 -@@ -247,7 +247,7 @@ noswitch: - BIC $15, R1 - MOVD R1, RSP - -- MOVW $CLOCK_MONOTONIC, R0 -+ MOVW $CLOCK_BOOTTIME, R0 - MOVD runtime·vdsoClockgettimeSym(SB), R2 - CBZ R2, fallback - BL (R2) -diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s -index 33ed1050c2..59a5be179c 100644 ---- a/src/runtime/sys_linux_mips64x.s -+++ b/src/runtime/sys_linux_mips64x.s -@@ -189,7 +189,7 @@ TEXT runtime·walltime(SB),NOSPLIT,$16 - RET - - TEXT runtime·nanotime(SB),NOSPLIT,$16 -- MOVW $1, R4 // CLOCK_MONOTONIC -+ MOVW $7, R4 // CLOCK_BOOTTIME - MOVV $0(R29), R5 - MOVV $SYS_clock_gettime, R2 - SYSCALL -diff --git a/src/runtime/sys_linux_mipsx.s b/src/runtime/sys_linux_mipsx.s -index 6e539fbc6f..55b2bf7156 100644 ---- a/src/runtime/sys_linux_mipsx.s -+++ b/src/runtime/sys_linux_mipsx.s -@@ -194,7 +194,7 @@ TEXT runtime·walltime(SB),NOSPLIT,$8-12 - RET - - TEXT runtime·nanotime(SB),NOSPLIT,$8-8 -- MOVW $1, R4 // CLOCK_MONOTONIC -+ MOVW $7, R4 // CLOCK_BOOTTIME - MOVW $4(R29), R5 - MOVW $SYS_clock_gettime, R2 - SYSCALL -diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s -index 13d23156bd..f67e5062aa 100644 ---- a/src/runtime/sys_linux_ppc64x.s -+++ b/src/runtime/sys_linux_ppc64x.s -@@ -204,7 +204,7 @@ fallback: - JMP finish - - TEXT runtime·nanotime(SB),NOSPLIT,$16 -- MOVD $1, R3 // CLOCK_MONOTONIC -+ MOVD $7, R3 // CLOCK_BOOTTIME - - MOVD R1, R15 // R15 is unchanged by C code - MOVD g_m(g), R21 // R21 = m -diff --git a/src/runtime/sys_linux_s390x.s b/src/runtime/sys_linux_s390x.s -index 58b36dff0a..cb92e9a402 100644 ---- a/src/runtime/sys_linux_s390x.s -+++ b/src/runtime/sys_linux_s390x.s -@@ -180,7 +180,7 @@ TEXT runtime·walltime(SB),NOSPLIT,$16 - RET - - TEXT runtime·nanotime(SB),NOSPLIT,$16 -- MOVW $1, R2 // CLOCK_MONOTONIC -+ MOVW $7, R2 // CLOCK_BOOTTIME - MOVD $tp-16(SP), R3 - MOVW $SYS_clock_gettime, R1 - SYSCALL --- -2.23.0 - diff --git a/app/tools/libwg-go/jni.c b/app/tools/libwg-go/jni.c deleted file mode 100644 index 3f877d47..00000000 --- a/app/tools/libwg-go/jni.c +++ /dev/null @@ -1,71 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * - * Copyright © 2017-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. - */ - -#include <jni.h> -#include <stdlib.h> -#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(); - -JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOn(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){ - .str = ifname_str, - .n = ifname_len - }, tun_fd, (struct go_string){ - .str = settings_str, - .n = settings_len - }); - (*env)->ReleaseStringUTFChars(env, ifname, ifname_str); - (*env)->ReleaseStringUTFChars(env, settings, settings_str); - return ret; -} - -JNIEXPORT void JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOff(JNIEnv *env, jclass c, jint handle) -{ - wgTurnOff(handle); -} - -JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgGetSocketV4(JNIEnv *env, jclass c, jint handle) -{ - return wgGetSocketV4(handle); -} - -JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgGetSocketV6(JNIEnv *env, jclass c, jint handle) -{ - return wgGetSocketV6(handle); -} - -JNIEXPORT jstring JNICALL Java_com_wireguard_android_backend_GoBackend_wgGetConfig(JNIEnv *env, jclass c, jint handle) -{ - jstring ret; - char *config = wgGetConfig(handle); - if (!config) - return NULL; - ret = (*env)->NewStringUTF(env, config); - free(config); - return ret; -} - -JNIEXPORT jstring JNICALL Java_com_wireguard_android_backend_GoBackend_wgVersion(JNIEnv *env, jclass c) -{ - jstring ret; - char *version = wgVersion(); - if (!version) - return NULL; - ret = (*env)->NewStringUTF(env, version); - free(version); - return ret; -} diff --git a/app/tools/ndk-compat/compat.c b/app/tools/ndk-compat/compat.c deleted file mode 100644 index 7cc99fc4..00000000 --- a/app/tools/ndk-compat/compat.c +++ /dev/null @@ -1,77 +0,0 @@ -/* SPDX-License-Identifier: BSD - * - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * - */ - -#define FILE_IS_EMPTY - -#if defined(__ANDROID_API__) && __ANDROID_API__ < 18 -#undef FILE_IS_EMPTY -#include <stdio.h> -#include <stdlib.h> - -ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) -{ - char *ptr, *eptr; - - if (*buf == NULL || *bufsiz == 0) { - *bufsiz = BUFSIZ; - if ((*buf = malloc(*bufsiz)) == NULL) - return -1; - } - - for (ptr = *buf, eptr = *buf + *bufsiz;;) { - int c = fgetc(fp); - if (c == -1) { - if (feof(fp)) { - ssize_t diff = (ssize_t)(ptr - *buf); - if (diff != 0) { - *ptr = '\0'; - return diff; - } - } - return -1; - } - *ptr++ = c; - if (c == delimiter) { - *ptr = '\0'; - return ptr - *buf; - } - if (ptr + 2 >= eptr) { - char *nbuf; - size_t nbufsiz = *bufsiz * 2; - ssize_t d = ptr - *buf; - if ((nbuf = realloc(*buf, nbufsiz)) == NULL) - return -1; - *buf = nbuf; - *bufsiz = nbufsiz; - eptr = nbuf + nbufsiz; - ptr = nbuf + d; - } - } -} - -ssize_t getline(char **buf, size_t *bufsiz, FILE *fp) -{ - return getdelim(buf, bufsiz, '\n', fp); -} -#endif - -#if defined(__ANDROID_API__) && __ANDROID_API__ < 24 -#undef FILE_IS_EMPTY -#include <string.h> - -char *strchrnul(const char *s, int c) -{ - char *x = strchr(s, c); - if (!x) - return (char *)s + strlen(s); - return x; -} -#endif - -#ifdef FILE_IS_EMPTY -#undef FILE_IS_EMPTY -static char ____x __attribute__((unused)); -#endif diff --git a/app/tools/ndk-compat/compat.h b/app/tools/ndk-compat/compat.h deleted file mode 100644 index 52f6c127..00000000 --- a/app/tools/ndk-compat/compat.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: BSD - * - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * - */ - -#if defined(__ANDROID_API__) && __ANDROID_API__ < 18 -#include <stdio.h> -ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp); -ssize_t getline(char **buf, size_t *bufsiz, FILE *fp); -#endif - -#if defined(__ANDROID_API__) && __ANDROID_API__ < 24 -char *strchrnul(const char *s, int c); -#endif - diff --git a/app/tools/wireguard-tools b/app/tools/wireguard-tools deleted file mode 160000 -Subproject e5b08c2849256367fc6bf37be9e737bdb21ee66 |