diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2018-02-07 19:19:20 +0100 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2018-02-17 21:14:09 +0100 |
commit | 0ea6f73332cf48374b8a98b16d68e25217ebf068 (patch) | |
tree | 53694268b30823c8973d6ae280ed766f4b44b974 /app/src/main/java | |
parent | b923f7bc57c39687bed225badeac0b35a7dee855 (diff) |
GoBackend: integrate into app
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'app/src/main/java')
5 files changed, 152 insertions, 38 deletions
diff --git a/app/src/main/java/com/wireguard/android/Application.java b/app/src/main/java/com/wireguard/android/Application.java index 4676e0bc..c1b21373 100644 --- a/app/src/main/java/com/wireguard/android/Application.java +++ b/app/src/main/java/com/wireguard/android/Application.java @@ -8,6 +8,7 @@ import android.os.Looper; import android.preference.PreferenceManager; import com.wireguard.android.backend.Backend; +import com.wireguard.android.backend.GoBackend; import com.wireguard.android.backend.WgQuickBackend; import com.wireguard.android.configStore.ConfigStore; import com.wireguard.android.configStore.FileConfigStore; @@ -16,6 +17,7 @@ import com.wireguard.android.util.AsyncWorker; import com.wireguard.android.util.RootShell; import com.wireguard.android.util.ToolsInstaller; +import java.io.File; import java.util.concurrent.Executor; import javax.inject.Qualifier; @@ -56,6 +58,8 @@ public class Application extends android.app.Application { ToolsInstaller getToolsInstaller(); TunnelManager getTunnelManager(); + + Class getBackendType(); } @Qualifier @@ -83,7 +87,16 @@ public class Application extends android.app.Application { public static Backend getBackend(@ApplicationContext final Context context, final RootShell rootShell, final ToolsInstaller toolsInstaller) { - return new WgQuickBackend(context, rootShell, toolsInstaller); + if (new File("/sys/module/wireguard").exists()) + return new WgQuickBackend(context, rootShell, toolsInstaller); + else + return new GoBackend(context); + } + + @ApplicationScope + @Provides + public static Class getBackendType(final Backend backend) { + return backend.getClass(); } @ApplicationScope diff --git a/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java b/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java index 3eb633ff..e3cd46b1 100644 --- a/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java +++ b/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java @@ -2,9 +2,12 @@ package com.wireguard.android.activity; import android.app.Activity; import android.os.Bundle; +import android.preference.Preference; import android.preference.PreferenceFragment; +import com.wireguard.android.Application; import com.wireguard.android.R; +import com.wireguard.android.backend.WgQuickBackend; /** * Interface for changing application-global persistent settings. @@ -26,6 +29,11 @@ public class SettingsActivity extends Activity { public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); + if (Application.getComponent().getBackendType() != WgQuickBackend.class) { + final Preference toolsInstaller = + getPreferenceManager().findPreference("tools_installer"); + getPreferenceScreen().removePreference(toolsInstaller); + } } } } diff --git a/app/src/main/java/com/wireguard/android/backend/GoBackend.java b/app/src/main/java/com/wireguard/android/backend/GoBackend.java new file mode 100644 index 00000000..db059e22 --- /dev/null +++ b/app/src/main/java/com/wireguard/android/backend/GoBackend.java @@ -0,0 +1,125 @@ +package com.wireguard.android.backend; + +import android.content.Context; +import android.support.v4.util.ArraySet; +import android.util.Log; + +import com.wireguard.android.model.Tunnel; +import com.wireguard.android.model.Tunnel.State; +import com.wireguard.android.model.Tunnel.Statistics; +import com.wireguard.config.Config; +import com.wireguard.config.Interface; +import com.wireguard.config.Peer; +import com.wireguard.crypto.KeyEncoding; + +import java.util.Collections; +import java.util.Formatter; +import java.util.Set; + +public final class GoBackend implements Backend { + private static final String TAG = "WireGuard/" + GoBackend.class.getSimpleName(); + + static { + System.loadLibrary("wg-go"); + } + + private final Context context; + private Tunnel currentTunnel; + + public GoBackend(final Context context) { + this.context = context; + } + + 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); + + @Override + public Config applyConfig(final Tunnel tunnel, final Config config) throws Exception { + if (tunnel.getState() == State.UP) { + // Restart the tunnel to apply the new config. + setStateInternal(tunnel, tunnel.getConfig(), State.DOWN); + try { + setStateInternal(tunnel, config, State.UP); + } catch (final Exception e) { + // The new configuration didn't work, so try to go back to the old one. + setStateInternal(tunnel, tunnel.getConfig(), State.UP); + throw e; + } + } + return config; + } + + @Override + public Set<String> enumerate() { + 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) { + return new Statistics(); + } + + @Override + public State setState(final Tunnel tunnel, State state) throws Exception { + final State originalState = getState(tunnel); + if (state == State.TOGGLE) + state = originalState == State.UP ? State.DOWN : State.UP; + if (state == originalState) + return originalState; + if (state == State.UP && currentTunnel != null) + throw new IllegalStateException("Only one userspace tunnel can run at a time"); + Log.d(TAG, "Changing tunnel " + tunnel.getName() + " to state " + state); + setStateInternal(tunnel, tunnel.getConfig(), state); + return getState(tunnel); + } + + private void setStateInternal(final Tunnel tunnel, final Config config, final State state) + throws Exception { + if (state == State.UP) { + // Do something (context.startService()...). + currentTunnel = tunnel; + + Formatter fmt = new Formatter(new StringBuilder()); + final Interface iface = config.getInterface(); + fmt.format("replace_peers=true\n"); + if (iface.getPrivateKey() != null) + fmt.format("private_key=%s\n", KeyEncoding.keyToHex(KeyEncoding.keyFromBase64(iface.getPrivateKey()))); + if (iface.getListenPort() != null) + fmt.format("listen_port=%d\n", Integer.parseInt(config.getInterface().getListenPort())); + for (final Peer peer : config.getPeers()) { + if (peer.getPublicKey() != null) + fmt.format("public_key=%s\n", KeyEncoding.keyToHex(KeyEncoding.keyFromBase64(peer.getPublicKey()))); + if (peer.getPreSharedKey() != null) + fmt.format("preshared_key=%s\n", KeyEncoding.keyToHex(KeyEncoding.keyFromBase64(peer.getPreSharedKey()))); + if (peer.getEndpoint() != null) + fmt.format("endpoint=%s\n", peer.getEndpoint()); + if (peer.getPersistentKeepalive() != null) + fmt.format("persistent_keepalive_interval=%d\n", Integer.parseInt(peer.getPersistentKeepalive())); + if (peer.getAllowedIPs() != null) { + for (final String allowedIp : peer.getAllowedIPs().split(" *, *")) { + fmt.format("allowed_ip=%s\n", allowedIp); + } + } + } + wgTurnOn(tunnel.getName(), -1, fmt.toString()); + } else { + // Do something else. + currentTunnel = null; + } + } +} diff --git a/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java b/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java index 907f5d6d..760aac85 100644 --- a/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java +++ b/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java @@ -88,8 +88,6 @@ public final class WgQuickBackend implements Backend { state = originalState == State.UP ? State.DOWN : State.UP; if (state == originalState) return originalState; - if (state == State.UP && !new File("/sys/module/wireguard").exists()) - throw new ModuleNotLoadedException("WireGuard module not loaded"); Log.d(TAG, "Changing tunnel " + tunnel.getName() + " to state " + state); toolsInstaller.ensureToolsAvailable(); setStateInternal(tunnel, tunnel.getConfig(), state); @@ -113,14 +111,4 @@ public final class WgQuickBackend implements Backend { if (result != 0) throw new Exception("Unable to configure tunnel (wg-quick returned " + result + ')'); } - - public static class ModuleNotLoadedException extends Exception { - public ModuleNotLoadedException(final String message, final Throwable cause) { - super(message, cause); - } - - public ModuleNotLoadedException(final String message) { - super(message); - } - } } diff --git a/app/src/main/java/com/wireguard/android/fragment/TunnelController.java b/app/src/main/java/com/wireguard/android/fragment/TunnelController.java index ca40654e..990509e0 100644 --- a/app/src/main/java/com/wireguard/android/fragment/TunnelController.java +++ b/app/src/main/java/com/wireguard/android/fragment/TunnelController.java @@ -1,18 +1,13 @@ package com.wireguard.android.fragment; -import android.app.AlertDialog; import android.content.Context; import android.databinding.DataBindingUtil; import android.databinding.ViewDataBinding; -import android.text.Html; -import android.text.method.LinkMovementMethod; import android.util.Log; import android.view.View; -import android.widget.TextView; import com.commonsware.cwac.crossport.design.widget.Snackbar; import com.wireguard.android.R; -import com.wireguard.android.backend.WgQuickBackend; import com.wireguard.android.databinding.TunnelDetailFragmentBinding; import com.wireguard.android.databinding.TunnelListItemBinding; import com.wireguard.android.model.Tunnel; @@ -47,26 +42,11 @@ public final class TunnelController { if (throwable == null) return; final Context context = view.getContext(); - if (ExceptionLoggers.unwrap(throwable) - instanceof WgQuickBackend.ModuleNotLoadedException) { - final String message = context.getString(R.string.not_supported_message); - final String title = context.getString(R.string.not_supported_title); - final AlertDialog dialog = new AlertDialog.Builder(context) - .setMessage(Html.fromHtml(message)) - .setPositiveButton(R.string.ok, null) - .setTitle(title) - .show(); - // Make links work. - ((TextView) dialog.findViewById(android.R.id.message)) - .setMovementMethod(LinkMovementMethod.getInstance()); - Log.e(TAG, title, throwable); - } else { - final String error = ExceptionLoggers.unwrap(throwable).getMessage(); - final int messageResId = checked ? R.string.error_up : R.string.error_down; - final String message = context.getString(messageResId, error); - Snackbar.make(view, message, Snackbar.LENGTH_LONG).show(); - Log.e(TAG, message, throwable); - } + final String error = ExceptionLoggers.unwrap(throwable).getMessage(); + final int messageResId = checked ? R.string.error_up : R.string.error_down; + final String message = context.getString(messageResId, error); + Snackbar.make(view, message, Snackbar.LENGTH_LONG).show(); + Log.e(TAG, message, throwable); }); } } |