diff options
Diffstat (limited to 'tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java')
-rw-r--r-- | tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java | 214 |
1 files changed, 158 insertions, 56 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 e8148c04..375de1f2 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java @@ -28,9 +28,12 @@ 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.DhcpRequest; +import com.wireguard.android.backend.gen.DhcpResponse; import com.wireguard.android.backend.gen.GetConnectionOwnerUidResponse; import com.wireguard.android.backend.gen.IpcSetRequest; import com.wireguard.android.backend.gen.IpcSetResponse; +import com.wireguard.android.backend.gen.Lease; import com.wireguard.android.backend.gen.LibwgGrpc; import com.wireguard.android.backend.gen.ReverseRequest; import com.wireguard.android.backend.gen.ReverseResponse; @@ -71,6 +74,7 @@ import java.net.UnknownHostException; import java.net.URL; import java.nio.ByteOrder; import java.util.Collections; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -103,7 +107,9 @@ public final class GoBackend implements Backend { private ManagedChannel channel; private ConnectivityManager connectivityManager; private ConnectivityManager.NetworkCallback myNetworkCallback = new MyNetworkCallback(); + private ConnectivityManager.NetworkCallback vpnNetworkCallback; @Nullable private Network activeNetwork; + private boolean obtainDhcpLease = false; /** * Public constructor for GoBackend. @@ -140,6 +146,8 @@ public final class GoBackend implements Backend { private static native int wgGetSocketV6(int handle); + private static native void wgSetFd(int handle, int tunFd); + private static native void wgTurnOff(int handle); private static native int wgTurnOn(String ifName, int tunFd, String settings); @@ -344,6 +352,34 @@ public final class GoBackend implements Backend { } } + private void Dhcp(VpnService service) throws Exception{ + obtainDhcpLease = false; + + LibwgGrpc.LibwgBlockingStub stub = LibwgGrpc.newBlockingStub(channel); + DhcpRequest request = DhcpRequest.newBuilder().build(); + DhcpResponse resp = stub.dhcp(request); + Log.i(TAG, "Dhcp: " + resp.getError().getMessage()); + + // Replace the vpn tunnel + final VpnService.Builder builder = getBuilder(currentTunnel.getName(), currentConfig, service, resp.getLeasesList()); + + Log.i(TAG, "Builder: " + builder); + + try (final ParcelFileDescriptor tun = builder.establish()) { + if (tun == null) + throw new BackendException(Reason.TUN_CREATION_ERROR); + Log.d(TAG, "Go backend " + wgVersion()); + // SetFd + wgSetFd(currentTunnelHandle, tun.detachFd()); + } + if (currentTunnelHandle < 0) + throw new BackendException(Reason.GO_ACTIVATION_ERROR_CODE, currentTunnelHandle); + + service.protect(wgGetSocketV4(currentTunnelHandle)); + service.protect(wgGetSocketV6(currentTunnelHandle)); + Log.i(TAG, "Dhcp done"); + } + private int getConnectionOwnerUid(int protocol, InetSocketAddress local, InetSocketAddress remote) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) return connectivityManager.getConnectionOwnerUid(protocol, local, remote); @@ -426,6 +462,94 @@ public final class GoBackend implements Backend { Log.i(TAG, "Exit streamReverse"); } + private VpnService.Builder getBuilder(final String name, @Nullable final Config config, final VpnService service, @Nullable final List<Lease> leases) throws PackageManager.NameNotFoundException { + Log.i(TAG, "Builder 1"); + final VpnService.Builder builder = service.getBuilder(); + Log.i(TAG, "Builder 2"); + builder.setSession(name); + + Log.i(TAG, "Builder 3"); + for (final String excludedApplication : config.getInterface().getExcludedApplications()) + builder.addDisallowedApplication(excludedApplication); + + Log.i(TAG, "Builder 4"); + for (final String includedApplication : config.getInterface().getIncludedApplications()) + builder.addAllowedApplication(includedApplication); + + Log.i(TAG, "Builder 5"); + if (leases != null) { + for (final Lease lease: leases) { + try { + InetAddress addr = InetAddress.getByAddress(lease.getAddress().getAddress().toByteArray()); + Log.i(TAG, "Lease: " + addr); + builder.addAddress(addr, 128); + } catch (UnknownHostException ex) { + // Ignore + } + } + } + + Log.i(TAG, "Builder 6"); + for (final InetNetwork addr : config.getInterface().getAddresses()) + builder.addAddress(addr.getAddress(), addr.getMask()); + + Log.i(TAG, "Builder 7"); + for (final InetAddress addr : config.getInterface().getDnsServers()) + builder.addDnsServer(addr.getHostAddress()); + + Log.i(TAG, "Builder 8"); + for (final String dnsSearchDomain : config.getInterface().getDnsSearchDomains()) + builder.addSearchDomain(dnsSearchDomain); + + Log.i(TAG, "Builder 9"); + boolean sawDefaultRoute = false; + for (final Peer peer : config.getPeers()) { + for (final InetNetwork addr : peer.getAllowedIps()) { + if (addr.getMask() == 0) + sawDefaultRoute = true; + builder.addRoute(addr.getAddress(), addr.getMask()); + } + } + + Log.i(TAG, "Builder 10"); + // "Kill-switch" semantics + if (!(sawDefaultRoute && config.getPeers().size() == 1)) { + builder.allowFamily(OsConstants.AF_INET); + builder.allowFamily(OsConstants.AF_INET6); + } + + Log.i(TAG, "Builder 11"); + builder.setMtu(config.getInterface().getMtu().orElse(1280)); + + Log.i(TAG, "Builder 12"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) + builder.setMetered(false); + Log.i(TAG, "Builder 13"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + service.setUnderlyingNetworks(null); + + Log.i(TAG, "Builder 14"); + // 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); + // } + // } + // } + + Log.i(TAG, "Builder 15"); + builder.setBlocking(true); + return builder; + } + private void setStateInternal(final Tunnel tunnel, @Nullable final Config config, final State state) throws Exception { Log.i(TAG, "Bringing tunnel " + tunnel.getName() + ' ' + state); @@ -486,63 +610,8 @@ public final class GoBackend implements Backend { final String goConfig = config.toWgUserspaceString(resolver); // Create the vpn tunnel with android API - final VpnService.Builder builder = service.getBuilder(); - builder.setSession(tunnel.getName()); - - for (final String excludedApplication : config.getInterface().getExcludedApplications()) - builder.addDisallowedApplication(excludedApplication); - - for (final String includedApplication : config.getInterface().getIncludedApplications()) - builder.addAllowedApplication(includedApplication); - - for (final InetNetwork addr : config.getInterface().getAddresses()) - builder.addAddress(addr.getAddress(), addr.getMask()); - - for (final InetAddress addr : config.getInterface().getDnsServers()) - builder.addDnsServer(addr.getHostAddress()); - - for (final String dnsSearchDomain : config.getInterface().getDnsSearchDomains()) - builder.addSearchDomain(dnsSearchDomain); - - boolean sawDefaultRoute = false; - for (final Peer peer : config.getPeers()) { - for (final InetNetwork addr : peer.getAllowedIps()) { - if (addr.getMask() == 0) - sawDefaultRoute = true; - builder.addRoute(addr.getAddress(), addr.getMask()); - } - } - - // "Kill-switch" semantics - if (!(sawDefaultRoute && config.getPeers().size() == 1)) { - builder.allowFamily(OsConstants.AF_INET); - builder.allowFamily(OsConstants.AF_INET6); - } - - builder.setMtu(config.getInterface().getMtu().orElse(1280)); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) - builder.setMetered(false); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) - service.setUnderlyingNetworks(null); - - 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); - } - } - } + final VpnService.Builder builder = getBuilder(tunnel.getName(), config, service, null); - builder.setBlocking(true); try (final ParcelFileDescriptor tun = builder.establish()) { if (tun == null) throw new BackendException(Reason.TUN_CREATION_ERROR); @@ -558,8 +627,14 @@ public final class GoBackend implements Backend { service.protect(wgGetSocketV4(currentTunnelHandle)); service.protect(wgGetSocketV6(currentTunnelHandle)); + obtainDhcpLease = true; + NetworkRequest req = new NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN).build(); connectivityManager.requestNetwork(req, myNetworkCallback); + + NetworkRequest vpnReq = new NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_VPN).removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN).build(); + vpnNetworkCallback = new VpnNetworkCallback(service); + connectivityManager.requestNetwork(vpnReq, vpnNetworkCallback); } else { if (currentTunnelHandle == -1) { Log.w(TAG, "Tunnel already down"); @@ -570,6 +645,9 @@ public final class GoBackend implements Backend { currentTunnelHandle = -1; currentConfig = null; stopHttpProxy(); + if (vpnNetworkCallback != null) + connectivityManager.unregisterNetworkCallback(vpnNetworkCallback); + vpnNetworkCallback = null; connectivityManager.unregisterNetworkCallback(myNetworkCallback); activeNetwork = null; wgTurnOff(handleToClose); @@ -638,6 +716,9 @@ public final class GoBackend implements Backend { final Tunnel tunnel = owner.currentTunnel; if (tunnel != null) { if (owner.currentTunnelHandle != -1) { + if (owner.vpnNetworkCallback != null) + owner.connectivityManager.unregisterNetworkCallback(owner.vpnNetworkCallback); + owner.vpnNetworkCallback = null; owner.connectivityManager.unregisterNetworkCallback(owner.myNetworkCallback); owner.activeNetwork = null; wgTurnOff(owner.currentTunnelHandle); @@ -668,6 +749,27 @@ public final class GoBackend implements Backend { } } + private class VpnNetworkCallback extends ConnectivityManager.NetworkCallback { + private VpnService service; + public VpnNetworkCallback(VpnService service) { + this.service = service; + } + @Override + public void onAvailable(Network network) { + Log.w(TAG, "VPN onAvailable: " + network); + if (obtainDhcpLease) { + Log.w(TAG, "Obtaindhcplease"); + try { + Log.w(TAG, "Before Dhcp"); + Dhcp(service); + Log.w(TAG, "After Dhcp"); + } catch (Exception ex) { + Log.e(TAG, "DHCP failed: " + ex); + } + } + } + } + private class MyNetworkCallback extends ConnectivityManager.NetworkCallback { @Override public void onAvailable(Network network) { |