From c2c3e13455ecfa310711e78aa830d14804aaaa5c Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sun, 16 Apr 2023 00:23:02 +0200 Subject: only take experimental transports into consideration in case the user switched the experimental transport toggle --- .../se/leap/bitmaskclient/eip/GatewaySelector.java | 1 + .../se/leap/bitmaskclient/eip/GatewaysManager.java | 13 ++- .../leap/bitmaskclient/eip/VpnConfigGenerator.java | 5 ++ .../bitmaskclient/eip/GatewaysManagerTest.java | 93 ++++++++++++++++++++++ .../bitmaskclient/eip/VpnConfigGeneratorTest.java | 27 +++++++ .../resources/ptdemo_obfs4hop_tcp_gateways.json | 3 +- 6 files changed, 138 insertions(+), 4 deletions(-) (limited to 'app') diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java index 52030ce3..ad95c823 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java @@ -55,6 +55,7 @@ public class GatewaySelector { return offsets.isEmpty() ? null : offsets.firstEntry().getValue().iterator().next(); } + // calculateOffsets randomizes the order of Gateways with the same distance, e.g. from the same location private TreeMap> calculateOffsets() { TreeMap> offsets = new TreeMap>(); int localOffset = getCurrentTimezone(); diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java index 28766998..d114665b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -17,6 +17,7 @@ package se.leap.bitmaskclient.eip; import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_HOP; import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN; import static de.blinkt.openvpn.core.connection.Connection.TransportType.PT; import static se.leap.bitmaskclient.base.models.Constants.GATEWAYS; @@ -121,6 +122,8 @@ public class GatewaysManager { private ArrayList locations = new ArrayList<>(); private TransportType selectedTransport; + GatewaySelector gatewaySelector; + public GatewaysManager(Context context) { this.context = context; configureFromCurrentProvider(); @@ -147,7 +150,7 @@ public class GatewaysManager { } public GatewayOptions select(int nClosest, String city) { - TransportType[] transportTypes = getUseBridges(context) ? new TransportType[]{OBFS4} : new TransportType[]{OPENVPN}; + TransportType[] transportTypes = getUseBridges(context) ? new TransportType[]{OBFS4, OBFS4_HOP} : new TransportType[]{OPENVPN}; if (presortedList.size() > 0) { return getGatewayFromPresortedList(nClosest, transportTypes, city); } @@ -276,7 +279,9 @@ public class GatewaysManager { private GatewayOptions getGatewayFromTimezoneCalculation(int nClosest, TransportType[] transportTypes, @Nullable String city) { List list = new ArrayList<>(gateways.values()); - GatewaySelector gatewaySelector = new GatewaySelector(list); + if (gatewaySelector == null) { + gatewaySelector = new GatewaySelector(list); + } Gateway gateway; int found = 0; int i = 0; @@ -341,7 +346,9 @@ public class GatewaysManager { private int getPositionFromTimezoneCalculatedList(VpnProfile profile) { TransportType transportType = profile.getTransportType(); - GatewaySelector gatewaySelector = new GatewaySelector(new ArrayList<>(gateways.values())); + if (gatewaySelector == null) { + gatewaySelector = new GatewaySelector(new ArrayList<>(gateways.values())); + } Gateway gateway; int nClosest = 0; int i = 0; diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java index 90ffb6b0..2c22d4f7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java @@ -140,7 +140,12 @@ public class VpnConfigGenerator { } if (apiVersion >= 3) { for (TransportType transportType : transports.keySet()) { + Transport transport = transports.get(transportType); if (transportType.isPluggableTransport()) { + Transport.Options transportOptions = transport.getOptions(); + if (!experimentalTransports && transportOptions != null && transportOptions.isExperimental()) { + continue; + } try { profiles.put(transportType, createProfile(transportType)); } catch (ConfigParser.ConfigParseError | NumberFormatException | JSONException | IOException e) { diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java index 998ed2fd..ee6ccce5 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java @@ -16,6 +16,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import de.blinkt.openvpn.VpnProfile; @@ -214,6 +215,88 @@ public class GatewaysManagerTest { assertEquals("37.12.247.10", gatewaysManager.select(0).gateway.getRemoteIP()); } + @Test + public void TestSelectN_select_includeExperimentalTransport_DecoupledPortHoppingGW() { + Provider provider = getProvider(null, null, null, null, null, null, "decoupled_pt_portHopping.eip-service.json", null); + + MockHelper.mockProviderObservable(provider); + mockStatic(PreferenceHelper.class); + when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true); + when(PreferenceHelper.allowExperimentalTransports(any(Context.class))).thenReturn(true); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + ArrayList hosts = new ArrayList<>(); + hosts.add(gatewaysManager.select(0).gateway.getHost()); + hosts.add(gatewaysManager.select(1).gateway.getHost()); + + assertTrue(hosts.contains("bridge-nyc1-02.bitmask-dev.leapvpn.net")); + assertTrue(hosts.contains("bridge-nyc1-01.bitmask-dev.leapvpn.net")); + + } + + @Test + public void TestSelectN_select_includeExperimentalTransport_DecoupledPortAndIPHoppingGW() { + Provider provider = getProvider(null, null, null, null, null, null, "decoupled_pt.eip-service.json", null); + + MockHelper.mockProviderObservable(provider); + mockStatic(PreferenceHelper.class); + when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true); + when(PreferenceHelper.allowExperimentalTransports(any(Context.class))).thenReturn(true); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + + ArrayList hosts = new ArrayList<>(); + hosts.add(gatewaysManager.select(0).gateway.getHost()); + hosts.add(gatewaysManager.select(1).gateway.getHost()); + assertTrue(hosts.contains("bridge-nyc1-02.bitmask-dev.leapvpn.net")); + assertTrue(hosts.contains("bridge-nyc1-01.bitmask-dev.leapvpn.net")); + } + + @Test + public void TestSelectN_select_excludeExperimentalTransport_DecoupledPortHoppingGW() { + Provider provider = getProvider(null, null, null, null, null, null, "decoupled_pt_portHopping.eip-service.json", null); + + MockHelper.mockProviderObservable(provider); + mockStatic(PreferenceHelper.class); + when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true); + when(PreferenceHelper.allowExperimentalTransports(any(Context.class))).thenReturn(false); + + for (int i = 0; i < 1000; i++) { + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + assertEquals("bridge-nyc1-01.bitmask-dev.leapvpn.net", gatewaysManager.select(0).gateway.getHost()); + } + } + + @Test + public void TestSelectN_select_excludeExperimentalTransport_DecoupledPortAndIPHoppingGW() { + Provider provider = getProvider(null, null, null, null, null, null, "decoupled_pt.eip-service.json", null); + + MockHelper.mockProviderObservable(provider); + mockStatic(PreferenceHelper.class); + when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true); + when(PreferenceHelper.allowExperimentalTransports(any(Context.class))).thenReturn(false); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + + assertEquals("bridge-nyc1-01.bitmask-dev.leapvpn.net", gatewaysManager.select(0).gateway.getHost()); + assertNull(gatewaysManager.select(1)); + } + + @Test + public void TestSelectN_select_excludeExperimentalTransport_InGatewayHoppingPTBridge() { + Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_obfs4hop_tcp_gateways.json", null); + + MockHelper.mockProviderObservable(provider); + mockStatic(PreferenceHelper.class); + when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true); + when(PreferenceHelper.allowExperimentalTransports(any(Context.class))).thenReturn(false); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + + ArrayList hosts = new ArrayList<>(); + hosts.add(gatewaysManager.select(0).gateway.getHost()); + hosts.add(gatewaysManager.select(1).gateway.getHost()); + + assertTrue(hosts.contains("pt.demo.bitmask.net")); + assertTrue(hosts.contains("manila.bitmask.net")); + } + @Test public void testSelectN_selectFromPresortedGateways_returnsGatewaysInPresortedOrder() { Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", "ptdemo_three_mixed_gateways.geoip.json"); @@ -555,6 +638,16 @@ public class GatewaysManagerTest { Provider provider = getProvider(null, null, null, null, null, null, "decoupled_pt.eip-service.json", null); MockHelper.mockProviderObservable(provider); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + assertEquals(2, gatewaysManager.size()); + } + + @Test + public void testGatewayManagerFromCurrentProvider_decoupledBridgesIncludingExperimental_threeGateways() throws IOException, NullPointerException { + Provider provider = getProvider(null, null, null, null, null, null, "decoupled_pt.eip-service.json", null); + MockHelper.mockProviderObservable(provider); + mockStatic(PreferenceHelper.class); + when(PreferenceHelper.allowExperimentalTransports(any(Context.class))).thenReturn(true); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); assertEquals(3, gatewaysManager.size()); } diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java index 565e6396..93231055 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java @@ -2,6 +2,7 @@ package se.leap.bitmaskclient.eip; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; @@ -33,6 +34,7 @@ import java.io.File; import java.util.HashMap; import de.blinkt.openvpn.VpnProfile; +import de.blinkt.openvpn.core.ConfigParser; import de.blinkt.openvpn.core.connection.Connection; import de.blinkt.openvpn.core.connection.Obfs4Connection; import de.blinkt.openvpn.core.connection.Obfs4HopConnection; @@ -2041,6 +2043,7 @@ public class VpnConfigGeneratorTest { generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_obfs4hop_tcp_gateways.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; + configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); assertTrue(vpnProfiles.containsKey(OBFS4_HOP) && ((Obfs4HopConnection)vpnProfiles.get(OBFS4_HOP).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("tcp")); @@ -2053,6 +2056,7 @@ public class VpnConfigGeneratorTest { generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_obfs4hop_kcp_gateways.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; + configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); assertTrue(vpnProfiles.containsKey(OBFS4_HOP) && ((Obfs4HopConnection)vpnProfiles.get(OBFS4_HOP).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("kcp")); @@ -2066,6 +2070,7 @@ public class VpnConfigGeneratorTest { generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt_portHopping.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; + configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); System.out.println(vpnProfiles.get(OBFS4_HOP).getConfigFile(context, false)); @@ -2079,6 +2084,7 @@ public class VpnConfigGeneratorTest { generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt_portHopping.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; + configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); System.out.println(vpnProfiles.get(OBFS4_HOP).getConfigFile(context, false)); @@ -2091,6 +2097,7 @@ public class VpnConfigGeneratorTest { generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; + configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); assertTrue(vpnProfiles.containsKey(OBFS4)); @@ -2105,6 +2112,7 @@ public class VpnConfigGeneratorTest { generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; + configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); assertTrue(vpnProfiles.containsKey(OBFS4_HOP)); @@ -2113,6 +2121,24 @@ public class VpnConfigGeneratorTest { assertFalse(vpnProfiles.containsKey(OBFS4)); } + @Test + public void testGenerateVpnProfile_noExperimental_skipObfs4Hop() throws Exception { + when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt.eip-service.json"))).getJSONArray("gateways").getJSONObject(2); + generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); + VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); + configuration.apiVersion = 3; + configuration.experimentalTransports = false; + vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); + Exception exception = null; + try { + vpnConfigGenerator.generateVpnProfiles(); + } catch (ConfigParser.ConfigParseError e) { + exception = e; + } + assertNotNull(exception); + } + @Test public void testGenerateVpnProfile_obfs4hop_onlyPortHopping_decoupled() throws Exception { when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); @@ -2120,6 +2146,7 @@ public class VpnConfigGeneratorTest { generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt_portHopping.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; + configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); assertTrue(vpnProfiles.containsKey(OBFS4_HOP)); diff --git a/app/src/test/resources/ptdemo_obfs4hop_tcp_gateways.json b/app/src/test/resources/ptdemo_obfs4hop_tcp_gateways.json index 6a748a4f..34bcecb3 100644 --- a/app/src/test/resources/ptdemo_obfs4hop_tcp_gateways.json +++ b/app/src/test/resources/ptdemo_obfs4hop_tcp_gateways.json @@ -66,7 +66,8 @@ { "type":"openvpn", "protocols":[ - "tcp" + "tcp", + "udp" ], "ports":[ "1195" -- cgit v1.2.3