GoBackend: Use the android VpnService to encapsulate the go backend

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Aurélien Chabot 2018-02-19 08:16:26 +11:00 committed by Jason A. Donenfeld
parent 19aec7c5c6
commit 9dfab4d60f
3 changed files with 99 additions and 7 deletions

View File

@ -50,6 +50,14 @@
</intent-filter>
</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"

View File

@ -1,11 +1,13 @@
package com.wireguard.android.activity;
import android.app.Activity;
import android.content.Intent;
import android.databinding.CallbackRegistry;
import android.databinding.CallbackRegistry.NotifierCallback;
import android.os.Bundle;
import com.wireguard.android.Application;
import com.wireguard.android.backend.GoBackend;
import com.wireguard.android.model.Tunnel;
import com.wireguard.android.model.TunnelManager;
@ -42,8 +44,16 @@ public abstract class BaseActivity extends Activity {
final TunnelManager tunnelManager = Application.getComponent().getTunnelManager();
selectedTunnel = tunnelManager.getTunnels().get(savedTunnelName);
}
// The selected tunnel must be set before the superclass method recreates fragments.
super.onCreate(savedInstanceState);
if (Application.getComponent().getBackendType() == GoBackend.class) {
Intent intent = GoBackend.VpnService.prepare(this);
if (intent != null) {
startActivityForResult(intent, 0);
}
}
}
@Override

View File

@ -1,6 +1,8 @@
package com.wireguard.android.backend;
import android.content.Context;
import android.content.Intent;
import android.os.ParcelFileDescriptor;
import android.support.v4.util.ArraySet;
import android.util.Log;
@ -23,13 +25,36 @@ public final class GoBackend implements Backend {
System.loadLibrary("wg-go");
}
private final Context context;
private Tunnel currentTunnel;
private int currentTunnelHandle = -1;
public GoBackend(final Context context) {
private Context context;
public GoBackend(Context context) {
this.context = context;
context.startService(new Intent(context, VpnService.class));
}
public static class VpnService extends android.net.VpnService {
@Override
public void onCreate() {
super.onCreate();
vpnService = this;
}
@Override
public void onDestroy() {
super.onDestroy();
vpnService = null;
}
public Builder getBuilder() {
return new Builder();
}
}
private static VpnService vpnService = null;
private static native int wgGetSocketV4(int handle);
private static native int wgGetSocketV6(int handle);
@ -90,10 +115,22 @@ public final class GoBackend implements Backend {
private void setStateInternal(final Tunnel tunnel, final Config config, final State state)
throws Exception {
if (state == State.UP) {
// Do something (context.startService()...).
currentTunnel = tunnel;
if (state == State.UP) {
Log.i(TAG, "Bringing tunnel up");
if (VpnService.prepare(context) != null)
throw new Exception("VPN service not authorized by user");
if (vpnService == null)
throw new Exception("Android VPN service is not running");
if (currentTunnelHandle != -1) {
Log.w(TAG, "Tunnel already up");
return;
}
// Build config
Formatter fmt = new Formatter(new StringBuilder());
final Interface iface = config.getInterface();
fmt.format("replace_peers=true\n");
@ -116,10 +153,47 @@ public final class GoBackend implements Backend {
}
}
}
wgTurnOn(tunnel.getName(), -1, fmt.toString());
// Create the vpn tunnel with android API
VpnService.Builder builder = vpnService.getBuilder();
builder.setSession(tunnel.getName());
builder.addAddress(config.getInterface().getAddress(), 32);
if (config.getInterface().getDns() != null)
builder.addDnsServer(config.getInterface().getDns());
for (final Peer peer : config.getPeers()) {
if (peer.getAllowedIPs() != null) {
for (final String allowedIp : peer.getAllowedIPs().split(" *, *")) {
String[] part = allowedIp.split("/", 2);
builder.addRoute(part[0], Integer.parseInt(part[1]));
}
}
}
builder.setBlocking(true);
ParcelFileDescriptor tun = builder.establish();
if (tun == null)
throw new Exception("Unable to create tun device");
currentTunnelHandle = wgTurnOn(tunnel.getName(), tun.detachFd(), fmt.toString());
if (currentTunnelHandle < 0)
throw new Exception("Unable to turn tunnel on (wgTurnOn return " + currentTunnelHandle + ")");
currentTunnel = tunnel;
vpnService.protect(wgGetSocketV4(currentTunnelHandle));
vpnService.protect(wgGetSocketV6(currentTunnelHandle));
} else {
// Do something else.
Log.i(TAG, "Bringing tunnel down");
if (currentTunnelHandle == -1) {
Log.w(TAG, "Tunnel already down");
return;
}
wgTurnOff(currentTunnelHandle);
currentTunnel = null;
currentTunnelHandle = -1;
}
}
}