diff options
7 files changed, 163 insertions, 20 deletions
diff --git a/tunnel/src/main/java/com/wireguard/android/backend/Backend.java b/tunnel/src/main/java/com/wireguard/android/backend/Backend.java index 4c18d98b..9afda719 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/Backend.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/Backend.java @@ -19,6 +19,17 @@ import androidx.annotation.Nullable; @NonNullForAll public interface Backend { /** + * Update the volatile configuration of a running tunnel and return the resulting configuration. + * If the tunnel is not up, return the configuration that would result (if known), or else + * simply return the given configuration. + * + * @param tunnel The tunnel to apply the configuration to. + * @param config The new configuration for this tunnel. + * @return The updated configuration of the tunnel. + */ + Config applyUserspaceConfig(ObservableTunnel tunnel, Config config) throws Exception; + + /** * Enumerate names of currently-running tunnels. * * @return The set of running tunnel names. diff --git a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java index 978b39aa..952a30f0 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java @@ -61,9 +61,26 @@ public final class GoBackend implements Backend { private static native int wgTurnOn(String ifName, int tunFd, String settings); + private static native String wgGetOperation(int handle); + + private static native int wgSetOperation(int handle, String settings); + private static native String wgVersion(); @Override + public Config applyUserspaceConfig(final ObservableTunnel tunnel, final Config config) throws Exception { + if (currentTunnelHandle != -1) { + // Build config + final String goConfig = config.toWgUserspaceString(); + + Log.d(TAG, "Go backend v" + wgVersion()); + wgSetOperation(currentTunnelHandle, goConfig); + Log.d(TAG, "Settings " + wgGetOperation(currentTunnelHandle)); + } + return config; + } + + @Override public Set<String> getRunningTunnelNames() { if (currentTunnel != null) { final Set<String> runningTunnels = new ArraySet<>(); @@ -216,6 +233,7 @@ public final class GoBackend implements Backend { throw new BackendException(Reason.TUN_CREATION_ERROR); Log.d(TAG, "Go backend v" + wgVersion()); currentTunnelHandle = wgTurnOn(tunnel.getName(), tun.detachFd(), goConfig); + Log.d(TAG, "Settings " + wgGetOperation(currentTunnelHandle)); } if (currentTunnelHandle < 0) throw new BackendException(Reason.GO_ACTIVATION_ERROR_CODE, currentTunnelHandle); diff --git a/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java b/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java index 78d7d5b6..c6a63691 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java @@ -55,6 +55,11 @@ public final class WgQuickBackend implements Backend { } @Override + public Config applyUserspaceConfig(final ObservableTunnel tunnel, final Config config) throws Exception { + throw new RuntimeException("Not implemented"); + } + + @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. diff --git a/tunnel/tools/libwg-go/api-android.go b/tunnel/tools/libwg-go/api-android.go index 7a393cae..9ccd913f 100644 --- a/tunnel/tools/libwg-go/api-android.go +++ b/tunnel/tools/libwg-go/api-android.go @@ -11,6 +11,7 @@ import "C" import ( "bufio" + "bytes" "golang.org/x/sys/unix" "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/ipc" @@ -143,6 +144,40 @@ func wgTurnOff(tunnelHandle int32) { handle.device.Close() } +//export wgGetOperation +func wgGetOperation(tunnelHandle int32) *C.char { + handle, ok := tunnelHandles[tunnelHandle] + if !ok { + return C.CString("(bad handle)") + } + + var buf bytes.Buffer + writer := bufio.NewWriter(&buf) + + getError := handle.device.IpcGetOperation(writer) + if getError != nil { + return C.CString(getError.Error()) + } + + writer.Flush() + return C.CString(buf.String()) +} + +//export wgSetOperation +func wgSetOperation(tunnelHandle int32, settings string) int32 { + handle, ok := tunnelHandles[tunnelHandle] + if !ok { + return -1 + } + + setError := handle.device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings))) + if setError != nil { + return -1 + } + + return 0 +} + //export wgGetSocketV4 func wgGetSocketV4(tunnelHandle int32) int32 { handle, ok := tunnelHandles[tunnelHandle] diff --git a/tunnel/tools/libwg-go/jni.c b/tunnel/tools/libwg-go/jni.c index 3f877d47..1cc69343 100644 --- a/tunnel/tools/libwg-go/jni.c +++ b/tunnel/tools/libwg-go/jni.c @@ -10,6 +10,8 @@ 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 char *wgGetOperation(int handle); +extern int wgSetOperation(int handle, struct go_string settings); extern int wgGetSocketV4(int handle); extern int wgGetSocketV6(int handle); extern char *wgGetConfig(int handle); @@ -38,6 +40,29 @@ JNIEXPORT void JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOff(JN wgTurnOff(handle); } +JNIEXPORT jstring JNICALL Java_com_wireguard_android_backend_GoBackend_wgGetOperation(JNIEnv *env, jclass c, jint handle) +{ + jstring ret; + char *settings = wgGetOperation(handle); + if (!settings) + return NULL; + ret = (*env)->NewStringUTF(env, settings); + free(settings); + return ret; +} + +JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgSetOperation(JNIEnv *env, jclass c, jint handle, jstring settings) +{ + const char *settings_str = (*env)->GetStringUTFChars(env, settings, 0); + size_t settings_len = (*env)->GetStringUTFLength(env, settings); + int ret = wgSetOperation(handle, (struct go_string){ + .str = settings_str, + .n = settings_len + }); + (*env)->ReleaseStringUTFChars(env, settings, settings_str); + return ret; +} + JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgGetSocketV4(JNIEnv *env, jclass c, jint handle) { return wgGetSocketV4(handle); diff --git a/ui/src/main/AndroidManifest.xml b/ui/src/main/AndroidManifest.xml index 5e993ae2..99a8f623 100644 --- a/ui/src/main/AndroidManifest.xml +++ b/ui/src/main/AndroidManifest.xml @@ -70,6 +70,9 @@ <action android:name="com.wireguard.android.action.REFRESH_TUNNEL_STATES" /> <action android:name="com.wireguard.android.action.SET_TUNNEL_UP" /> <action android:name="com.wireguard.android.action.SET_TUNNEL_DOWN" /> + <action android:name="com.wireguard.android.action.GET_TUNNEL_CONFIG" /> + <action android:name="com.wireguard.android.action.SET_TUNNEL_CONFIG" /> + <action android:name="com.wireguard.android.action.SET_TUNNEL_USERSPACE_CONFIG" /> </intent-filter> </receiver> diff --git a/ui/src/main/java/com/wireguard/android/model/TunnelManager.java b/ui/src/main/java/com/wireguard/android/model/TunnelManager.java index e370f8de..e1806c2f 100644 --- a/ui/src/main/java/com/wireguard/android/model/TunnelManager.java +++ b/ui/src/main/java/com/wireguard/android/model/TunnelManager.java @@ -8,6 +8,10 @@ package com.wireguard.android.model; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.util.Log; +import androidx.annotation.Nullable; +import androidx.databinding.BaseObservable; +import androidx.databinding.Bindable; import com.wireguard.android.Application; import com.wireguard.android.BR; @@ -19,9 +23,13 @@ import com.wireguard.android.configStore.ConfigStore; import com.wireguard.android.util.ExceptionLoggers; import com.wireguard.android.util.ObservableSortedKeyedArrayList; import com.wireguard.android.util.ObservableSortedKeyedList; +import com.wireguard.config.BadConfigException; import com.wireguard.config.Config; import com.wireguard.util.NonNullForAll; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; @@ -42,6 +50,7 @@ import java9.util.stream.StreamSupport; @NonNullForAll public final class TunnelManager extends BaseObservable { + private static final String TAG = "WireGuard/" + TunnelManager.class.getSimpleName(); private static final Comparator<String> COMPARATOR = Comparators.<String>thenComparing( String.CASE_INSENSITIVE_ORDER, Comparators.naturalOrder()); private static final String KEY_LAST_USED_TUNNEL = "last_used_tunnel"; @@ -215,6 +224,15 @@ public final class TunnelManager extends BaseObservable { }).thenApply(tunnel::onConfigChanged); } + CompletionStage<Config> setTunnelUserspaceConfig(final Tunnel tunnel, final Config config) { + Log.println(Log.INFO, TAG, "Called setTunnelUserspaceConfig " + config); + return Application.getAsyncWorker().supplyAsync(() -> { + Log.println(Log.INFO, TAG, "Async setTunnelUserspaceConfig " + config); + final Config appliedConfig = Application.getBackend().applyUserspaceConfig(tunnel, config); + return appliedConfig; + }); + } + CompletionStage<String> setTunnelName(final ObservableTunnel tunnel, final String name) { if (Tunnel.isNameInvalid(name)) return CompletableFuture.failedFuture(new IllegalArgumentException(context.getString(R.string.tunnel_error_invalid_name))); @@ -263,6 +281,7 @@ public final class TunnelManager extends BaseObservable { public static final class IntentReceiver extends BroadcastReceiver { @Override public void onReceive(final Context context, @Nullable final Intent intent) { + Log.println(Log.INFO, TAG, "Broadcast received " + intent); final TunnelManager manager = Application.getTunnelManager(); if (intent == null) return; @@ -275,29 +294,56 @@ public final class TunnelManager extends BaseObservable { return; } - /* We disable the below, for now, as the security model of allowing this - * might take a bit more consideration. - */ - if (true) - return; - - final State state; - if ("com.wireguard.android.action.SET_TUNNEL_UP".equals(action)) - state = State.UP; - else if ("com.wireguard.android.action.SET_TUNNEL_DOWN".equals(action)) - state = State.DOWN; - else - return; - final String tunnelName = intent.getStringExtra("tunnel"); if (tunnelName == null) return; - manager.getTunnels().thenAccept(tunnels -> { - final ObservableTunnel tunnel = tunnels.get(tunnelName); - if (tunnel == null) - return; - manager.setTunnelState(tunnel, state); - }); + + if ("com.wireguard.android.action.SET_TUNNEL_UP".equals(action) || + "com.wireguard.android.action.SET_TUNNEL_DOWN".equals(action)) { + final State state; + if ("com.wireguard.android.action.SET_TUNNEL_UP".equals(action)) + state = State.UP; + else /* "com.wireguard.android.action.SET_TUNNEL_DOWN".equals(action) */ + state = State.DOWN; + + manager.getTunnels().thenAccept(tunnels -> { + final ObservableTunnel tunnel = tunnels.get(tunnelName); + if (tunnel == null) + return; + manager.setTunnelState(tunnel, state); + }); + } else if ("com.wireguard.android.action.GET_TUNNEL_CONFIG".equals(action) || + "com.wireguard.android.action.SET_TUNNEL_CONFIG".equals(action) || + "com.wireguard.android.action.SET_TUNNEL_USERSPACE_CONFIG".equals(action)) { + final ActionCode code; + if ("com.wireguard.android.action.SET_TUNNEL_CONFIG".equals(action)) + code = ActionCode.SET_CONFIG; + else if ("com.wireguard.android.action.SET_TUNNEL_USERSPACE_CONFIG".equals(action)) + code = ActionCode.SET_USERSPACE_CONFIG; + else /* "com.wireguard.android.action.GET_TUNNEL_CONFIG".equals(action) */ + code = ActionCode.GET_CONFIG; + manager.getTunnels().thenAccept(tunnels -> { + final ObservableTunnel tunnel = tunnels.get(tunnelName); + if (tunnel == null) + return; + + final String config = intent.getStringExtra("config"); + try { + if (code == ActionCode.SET_USERSPACE_CONFIG) { + Log.println(Log.INFO, TAG, "Call setTunnelUserspaceConfig " + config); + manager.setTunnelUserspaceConfig(tunnel, Config.parse(new BufferedReader(new StringReader(config)))); + } else { + Log.println(Log.INFO, TAG, "Call setTunnelConfig " + config); + manager.setTunnelConfig(tunnel, Config.parse(new BufferedReader(new StringReader(config)))); + } + } catch (IOException | BadConfigException e) { + Log.println(Log.ERROR, TAG, Log.getStackTraceString(e)); + } + }); + } else + return; } } + + private enum ActionCode { GET_CONFIG, SET_CONFIG, SET_USERSPACE_CONFIG }; } |