GoBackend: integrate into app

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld
2018-02-17 21:14:09 +01:00
parent b923f7bc57
commit 0ea6f73332
15 changed files with 348 additions and 59 deletions
@@ -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
@@ -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);
}
}
}
}
@@ -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;
}
}
}
@@ -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);
}
}
}
@@ -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);
});
}
}
-17
View File
@@ -39,23 +39,6 @@
<string name="listen_port">Listen port</string>
<string name="mtu">MTU</string>
<string name="name">Name</string>
<string name="not_supported_message" tools:ignore="TypographyQuotes"><![CDATA[
<p>Your Android device does not <em>currently</em> have the WireGuard kernel module. Please
talk to the manufacturer of your Android device or the author of your device&rsquo;s ROM
about including the WireGuard kernel module.</p>
<p>Fortunately, we are in the process of implementing support for WireGuard in a way that
will work on all devices, without any need for the kernel module. This means that while you
may not be able to use WireGuard today, you will very likely be able to use WireGuard in
several weeks. Things are looking up!</p>
<p>Sorry for the wait. In the mean time, you may stay up to date on the latest project news
by <a href="https://lists.zx2c4.com/mailman/listinfo/wireguard">subscribing to our mailing
list</a>. General information about the project is available at
<a href="https://www.wireguard.com/">WireGuard.com</a>.</p>
]]></string>
<string name="not_supported_title">WireGuard not installed</string>
<string name="ok">OK</string>
<string name="peer">Peer</string>
<string name="persistent_keepalive">Persistent keepalive</string>
<string name="pre_shared_key">Pre-shared key</string>
+1 -1
View File
@@ -5,5 +5,5 @@
android:key="restore_on_boot"
android:summary="@string/restore_on_boot_summary"
android:title="@string/restore_on_boot_title" />
<com.wireguard.android.preference.ToolsInstallerPreference />
<com.wireguard.android.preference.ToolsInstallerPreference android:key="tools_installer" />
</PreferenceScreen>