summaryrefslogtreecommitdiffhomepage
path: root/tunnel/src/main/java/com/wireguard/android
diff options
context:
space:
mode:
authorMikael Magnusson <mikma@users.sourceforge.net>2022-03-29 23:51:21 +0200
committerMikael Magnusson <mikma@users.sourceforge.net>2023-08-19 00:30:43 +0200
commite22cc0235da95caee124a4a9bf5b7ec26b2012e3 (patch)
tree597252d48de8042be68770adf40c2a8760cfb4d8 /tunnel/src/main/java/com/wireguard/android
parent4de219e116c94179c9a8df3624114adc9712eebe (diff)
ui,tunnel: implement http proxy for Android 10+
Allow getting package names of tv apps.
Diffstat (limited to 'tunnel/src/main/java/com/wireguard/android')
-rw-r--r--tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java165
1 files changed, 163 insertions, 2 deletions
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 79c362b4..596bea7d 100644
--- a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
+++ b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
@@ -7,6 +7,7 @@ package com.wireguard.android.backend;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.LocalSocketAddress;
@@ -14,8 +15,10 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.ProxyInfo;
+import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.system.OsConstants;
import android.util.Log;
@@ -25,11 +28,19 @@ import com.google.protobuf.Empty;
import com.wireguard.android.backend.BackendException.Reason;
import com.wireguard.android.backend.Tunnel.State;
+import com.wireguard.android.backend.gen.GetConnectionOwnerUidResponse;
import com.wireguard.android.backend.gen.LibwgGrpc;
+import com.wireguard.android.backend.gen.ReverseRequest;
+import com.wireguard.android.backend.gen.ReverseResponse;
+import com.wireguard.android.backend.gen.StartHttpProxyRequest;
+import com.wireguard.android.backend.gen.StartHttpProxyResponse;
+import com.wireguard.android.backend.gen.StopHttpProxyRequest;
+import com.wireguard.android.backend.gen.StopHttpProxyResponse;
import com.wireguard.android.backend.gen.VersionRequest;
import com.wireguard.android.backend.gen.VersionResponse;
import com.wireguard.android.util.SharedLibraryLoader;
import com.wireguard.config.Config;
+import com.wireguard.config.HttpProxy;
import com.wireguard.config.InetEndpoint;
import com.wireguard.config.InetNetwork;
import com.wireguard.config.Peer;
@@ -42,18 +53,30 @@ import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.android.UdsChannelBuilder;
import io.grpc.okhttp.OkHttpChannelBuilder;
+import io.grpc.stub.StreamObserver;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
import java.net.URL;
import java.time.Instant;
+import java.nio.ByteOrder;
import java.util.Collections;
+import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
import javax.net.SocketFactory;
@@ -264,6 +287,130 @@ public final class GoBackend implements Backend {
return getState(tunnel);
}
+ private int startHttpProxy(Uri pacFileUrl) {
+ LibwgGrpc.LibwgStub asyncStub = LibwgGrpc.newStub(channel);
+ LibwgGrpc.LibwgBlockingStub stub = LibwgGrpc.newBlockingStub(channel);
+ StartHttpProxyRequest.Builder reqBuilder = StartHttpProxyRequest.newBuilder();
+ if (pacFileUrl != null && pacFileUrl != Uri.EMPTY) {
+ reqBuilder.setPacFileUrl(pacFileUrl.toString());
+ }
+
+ Thread streamer = new Thread(new Runnable() {
+ public void run() {
+ try {
+ Log.i(TAG, "Before streamReverse");
+ streamReverse(asyncStub);
+ Log.i(TAG, "After streamReverse");
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+
+ StartHttpProxyRequest req = reqBuilder.build();
+ StartHttpProxyResponse resp = stub.startHttpProxy(req);
+ Log.i(TAG, "Start http proxy listen_port:" + resp.getListenPort() + ", error:" + resp.getError().getMessage());
+ streamer.start();
+ return resp.getListenPort();
+ }
+
+ private void stopHttpProxy() {
+ LibwgGrpc.LibwgBlockingStub stub = LibwgGrpc.newBlockingStub(channel);
+ StopHttpProxyRequest req = StopHttpProxyRequest.newBuilder().build();
+ StopHttpProxyResponse resp = stub.stopHttpProxy(req);
+ Log.i(TAG, "Stop http proxy: " + resp.getError().getMessage());
+ }
+
+ private static InetSocketAddress toInetSocketAddress(com.wireguard.android.backend.gen.InetSocketAddress sockAddr) {
+ try {
+ return new InetSocketAddress(InetAddress.getByAddress(sockAddr.getAddress().getAddress().toByteArray()), sockAddr.getPort());
+ } catch (UnknownHostException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private int getConnectionOwnerUid(int protocol, InetSocketAddress local, InetSocketAddress remote) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
+ return connectivityManager.getConnectionOwnerUid(protocol, local, remote);
+ else
+ return Process.INVALID_UID;
+ }
+
+ private void streamReverse(LibwgGrpc.LibwgStub asyncStub) throws InterruptedException {
+ Log.i(TAG, "In streamReverse");
+ final CountDownLatch finishLatch = new CountDownLatch(1);
+ final AtomicReference<StreamObserver<ReverseRequest>> atomicRequestObserver = new AtomicReference<StreamObserver<ReverseRequest>>();
+ // Throwable failed = null;
+
+ StreamObserver<ReverseResponse> responseObserver = new StreamObserver<ReverseResponse>() {
+ @Override
+ public void onNext(ReverseResponse resp) {
+ try {
+ String pkg = "";
+ int uid = getConnectionOwnerUid(resp.getUid().getProtocol(), toInetSocketAddress(resp.getUid().getLocal()), toInetSocketAddress(resp.getUid().getRemote()));
+ if (uid != Process.INVALID_UID) {
+ PackageManager pm = context.getPackageManager();
+ pkg = pm.getNameForUid(uid);
+ String[] pkgs = pm.getPackagesForUid(uid);
+ Log.i(TAG, "reverse onNext uid:" + uid + " package:" + pkg);
+ if (pkgs != null) {
+ for (int i=0; i < pkgs.length; i++) {
+ Log.i(TAG, "getPackagesForUid() = " + pkgs[i]);
+ }
+ }
+ } else {
+ Log.i(TAG, "Connection not found");
+ }
+
+ ReverseRequest req = ReverseRequest.newBuilder()
+ .setUid(GetConnectionOwnerUidResponse.newBuilder()
+ .setUid(uid)
+ .setPackage(pkg != null ? pkg: "")
+ .build())
+ .build();
+
+ io.grpc.Context.current().fork().run(new Runnable() {
+ public void run() {
+ atomicRequestObserver.get().onNext(req);
+ }
+ });
+ } catch (RuntimeException ex) {
+ Log.i(TAG, "onNext " + ex);
+ throw ex;
+ }
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ // failed = t;
+ Log.i(TAG, "streamReverse error: " + t);
+ finishLatch.countDown();
+ }
+
+ @Override
+ public void onCompleted() {
+ Log.i(TAG, "streamReverse completed");
+ finishLatch.countDown();
+ }
+ };
+ StreamObserver<ReverseRequest> requestObserver = asyncStub.reverse(responseObserver);
+ atomicRequestObserver.set(requestObserver);
+
+ // Mark the end of requests
+ //requestObserver.onCompleted();
+
+ //requestObserver.onNext(ReverseRequest.getDefaultInstance());
+
+ Log.i(TAG, "Waiting streamReverse");
+ // Receiving happens asynchronously
+ finishLatch.await();
+
+ // if (failed != null) {
+ // throw new RuntimeException(failed);
+ // }
+ Log.i(TAG, "Exit streamReverse");
+ }
+
private void setStateInternal(final Tunnel tunnel, @Nullable final Config config, final State state)
throws Exception {
Log.i(TAG, "Bringing tunnel " + tunnel.getName() + ' ' + state);
@@ -367,8 +514,20 @@ public final class GoBackend implements Backend {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
service.setUnderlyingNetworks(null);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- config.getInterface().getHttpProxy().ifPresent(pi -> builder.setHttpProxy(pi.getProxyInfo()));
+ Optional<HttpProxy> proxy = config.getInterface().getHttpProxy();
+ if (proxy.isPresent()) {
+ ProxyInfo pi = proxy.get().getProxyInfo();
+ Uri pacFileUrl = pi.getPacFileUrl();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ if (pacFileUrl != null && pacFileUrl != Uri.EMPTY) {
+ int listenPort = startHttpProxy(pacFileUrl);
+ ProxyInfo localPi = ProxyInfo.buildDirectProxy("localhost", listenPort);
+ builder.setHttpProxy(localPi);
+ } else {
+ builder.setHttpProxy(pi);
+ }
+ }
}
builder.setBlocking(true);
@@ -398,6 +557,7 @@ public final class GoBackend implements Backend {
currentTunnel = null;
currentTunnelHandle = -1;
currentConfig = null;
+ stopHttpProxy();
connectivityManager.unregisterNetworkCallback(myNetworkCallback);
activeNetwork = null;
wgTurnOff(handleToClose);
@@ -465,6 +625,7 @@ public final class GoBackend implements Backend {
@Override
public void onDestroy() {
if (owner != null) {
+ owner.stopHttpProxy();
final Tunnel tunnel = owner.currentTunnel;
if (tunnel != null) {
if (owner.currentTunnelHandle != -1) {