diff options
6 files changed, 138 insertions, 4 deletions
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<Integer, Set<Gateway>> calculateOffsets() { TreeMap<Integer, Set<Gateway>> offsets = new TreeMap<Integer, Set<Gateway>>(); 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<Location> 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<Gateway> 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; @@ -215,6 +216,88 @@ public class GatewaysManagerTest { } @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<String> 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<String> 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<String> 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<Connection.TransportType, VpnProfile> 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<Connection.TransportType, VpnProfile> 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<Connection.TransportType, VpnProfile> 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<Connection.TransportType, VpnProfile> 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<Connection.TransportType, VpnProfile> 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<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); assertTrue(vpnProfiles.containsKey(OBFS4_HOP)); @@ -2114,12 +2122,31 @@ public class VpnConfigGeneratorTest { } @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); gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt_portHopping.eip-service.json"))).getJSONArray("gateways").getJSONObject(2); 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<Connection.TransportType, VpnProfile> 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" |