diff options
5 files changed, 266 insertions, 8 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java index f4531ff8..94f737b4 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java @@ -9,12 +9,15 @@ import static se.leap.bitmaskclient.base.models.Constants.PREFER_UDP; import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES; import static se.leap.bitmaskclient.base.models.Constants.USE_IPv6_FIREWALL; +import static se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper.useObfsVpn; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.allowExperimentalTransports; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferUDP; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getShowAlwaysOnDialog; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseBridges; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseSnowflake; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.hasSnowflakePrefs; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.preferUDP; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setAllowExperimentalTransports; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useBridges; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useSnowflake; import static se.leap.bitmaskclient.base.utils.ViewHelper.setActionBarTitle; @@ -81,6 +84,7 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh initFirewallEntry(view); initTetheringEntry(view); initGatewayPinningEntry(view); + initExperimentalTransportsEntry(view); setActionBarTitle(this, advanced_settings); return view; } @@ -249,6 +253,23 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh }); } + public void initExperimentalTransportsEntry(View rootView) { + IconSwitchEntry experimentalTransports = rootView.findViewById(R.id.experimental_transports); + if (useObfsVpn() && ProviderObservable.getInstance().getCurrentProvider().supportsExperimentalPluggableTransports()) { + experimentalTransports.setVisibility(VISIBLE); + experimentalTransports.setChecked(allowExperimentalTransports(this.getContext())); + experimentalTransports.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (!buttonView.isPressed()) { + return; + } + setAllowExperimentalTransports(getContext(), isChecked); + }); + } else { + experimentalTransports.setVisibility(GONE); + } + + } + public void showTetheringAlert() { try { diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java index 02a9694a..7b8f22af 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java @@ -30,6 +30,7 @@ import java.net.URL; import java.util.Locale; import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_KCP; import static se.leap.bitmaskclient.base.models.Constants.CAPABILITIES; import static se.leap.bitmaskclient.base.models.Constants.GATEWAYS; import static se.leap.bitmaskclient.base.models.Constants.LOCATIONS; @@ -37,8 +38,12 @@ import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_ALLOWED_REGIS import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_ALLOW_ANONYMOUS; import static se.leap.bitmaskclient.base.models.Constants.TRANSPORT; import static se.leap.bitmaskclient.base.models.Constants.TYPE; +import static se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper.useObfsVpn; import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS; +import de.blinkt.openvpn.core.connection.Connection; +import de.blinkt.openvpn.core.connection.Connection.TransportType; + /** * @author Sean Leonard <meanderingcode@aetherislands.net> * @author Parménides GV <parmegv@sdf.org> @@ -161,6 +166,17 @@ public final class Provider implements Parcelable { } public boolean supportsPluggableTransports() { + if (useObfsVpn()) { + return supportsTransports(new TransportType[]{OBFS4, OBFS4_KCP}); + } + return supportsTransports(new TransportType[]{OBFS4}); + } + + public boolean supportsExperimentalPluggableTransports() { + return supportsTransports(new TransportType[]{OBFS4_KCP}); + } + + private boolean supportsTransports(TransportType[] transportTypes) { try { JSONArray gatewayJsons = eipServiceJson.getJSONArray(GATEWAYS); for (int i = 0; i < gatewayJsons.length(); i++) { @@ -168,15 +184,18 @@ public final class Provider implements Parcelable { getJSONObject(CAPABILITIES). getJSONArray(TRANSPORT); for (int j = 0; j < transports.length(); j++) { - if (OBFS4.toString().equals(transports.getJSONObject(j).getString(TYPE))) { - return true; + String supportedTransportType = transports.getJSONObject(j).getString(TYPE); + for (TransportType transportType : transportTypes) { + if (transportType.toString().equals(supportedTransportType)) { + return true; + } } } } } catch (Exception e) { - // ignore + // ignore } - return false; + return false; } public String getIpForHostname(String host) { diff --git a/app/src/main/res/layout/f_settings.xml b/app/src/main/res/layout/f_settings.xml index 7b8733cd..f89dc956 100644 --- a/app/src/main/res/layout/f_settings.xml +++ b/app/src/main/res/layout/f_settings.xml @@ -114,5 +114,14 @@ app:subtitle="Connect to a specific Gateway for debugging purposes" /> + <se.leap.bitmaskclient.base.views.IconSwitchEntry + android:id="@+id/experimental_transports" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:text="Experimental transports" + app:singleLine="false" + app:subtitle="These transports might circumvent censorship, but are still in a testing phase" + /> + </LinearLayout> </ScrollView>
\ No newline at end of file diff --git a/app/src/test/java/se/leap/bitmaskclient/base/models/ProviderTest.java b/app/src/test/java/se/leap/bitmaskclient/base/models/ProviderTest.java index aaf3f255..8bff690b 100644 --- a/app/src/test/java/se/leap/bitmaskclient/base/models/ProviderTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/base/models/ProviderTest.java @@ -1,21 +1,36 @@ package se.leap.bitmaskclient.base.models; +import static junit.framework.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.mockStatic; + +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; import java.util.HashSet; import java.util.Set; -import se.leap.bitmaskclient.base.models.Provider; +import se.leap.bitmaskclient.base.utils.ConfigHelper; import se.leap.bitmaskclient.testutils.TestSetupHelper; -import static junit.framework.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - /** * Created by cyberta on 12.02.18. */ + +@RunWith(PowerMockRunner.class) +@PrepareForTest({ConfigHelper.ObfsVpnHelper.class}) public class ProviderTest { + @Before + public void setup() { + mockStatic(ConfigHelper.ObfsVpnHelper.class); + when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(false); + } + @Test public void testEquals_sameFields_returnsTrue() throws Exception { Provider p1 = TestSetupHelper.getConfiguredProvider(); @@ -84,4 +99,64 @@ public class ProviderTest { assertFalse(p1.supportsPluggableTransports()); } + @Test + public void testIsExperimentalPluggableTransportsSupported_Obfs4_returnsFalse() throws Exception { + Provider p1 = TestSetupHelper.getProvider( + "https://pt.demo.bitmask.net", + null, + null, + null, + null, + null, + "ptdemo.bitmask.eip-service.json", + null); + assertFalse(p1.supportsExperimentalPluggableTransports()); + } + + @Test + public void testIsExperimentalPluggableTransportsSupported_Obfs4Kcp_returnsTrue() throws Exception { + Provider p1 = TestSetupHelper.getProvider( + "https://pt.demo.bitmask.net", + null, + null, + null, + null, + null, + "ptdemo_kcp_gateways.json", + null); + assertTrue(p1.supportsExperimentalPluggableTransports()); + } + + @Test + public void testSupportsPluggableTransports_Obfs4Kcp_noObsvpn_returnsFalse() throws Exception { + when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(false); + + Provider p1 = TestSetupHelper.getProvider( + "https://pt.demo.bitmask.net", + null, + null, + null, + null, + null, + "ptdemo_only_experimental_transports_gateways.json", + null); + assertFalse(p1.supportsPluggableTransports()); + } + + @Test + public void testSupportsPluggableTransports_Obfs4Kcp_obsvpn_returnsTrue() throws Exception { + when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); + + Provider p1 = TestSetupHelper.getProvider( + "https://pt.demo.bitmask.net", + null, + null, + null, + null, + null, + "ptdemo_only_experimental_transports_gateways.json", + null); + assertTrue(p1.supportsPluggableTransports()); + } + } diff --git a/app/src/test/resources/ptdemo_only_experimental_transports_gateways.json b/app/src/test/resources/ptdemo_only_experimental_transports_gateways.json new file mode 100644 index 00000000..31c919e7 --- /dev/null +++ b/app/src/test/resources/ptdemo_only_experimental_transports_gateways.json @@ -0,0 +1,134 @@ +{ + "gateways":[ + { + "capabilities":{ + "adblock":false, + "filter_dns":false, + "limited":false, + "transport":[ + { + "type":"obfs4-1", + "protocols":[ + "tcp" + ], + "ports":[ + "23050" + ], + "options": { + "cert": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1", + "iatMode": "0" + } + }, + { + "type":"openvpn", + "protocols":[ + "tcp" + ], + "ports":[ + "1195" + ] + } + ], + "user_ips":false + }, + "host":"pt.demo.bitmask.net", + "ip_address":"37.218.247.60", + "location":"Amsterdam" + }, + { + "capabilities":{ + "adblock":false, + "filter_dns":false, + "limited":false, + "transport":[ + { + "type":"openvpn", + "protocols":[ + "tcp" + ], + "ports":[ + "1195" + ] + } + ], + "user_ips":false + }, + "host":"moscow.bitmask.net", + "ip_address":"3.21.247.89", + "location":"moscow" + }, + { + "capabilities":{ + "adblock":false, + "filter_dns":false, + "limited":false, + "transport":[ + { + "type":"openvpn", + "protocols":[ + "tcp", + "udp" + ], + + "ports":[ + "1195" + ] + }, + { + "type":"obfs4-1", + "protocols":[ + "tcp" + ], + "ports":[ + "23050" + ], + "options": { + "cert": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1", + "iatMode": "0" + } + } + ], + "user_ips":false + }, + "host":"manila.bitmask.net", + "ip_address":"37.12.247.10", + "location":"manila" + } + ], + "locations":{ + "Amsterdam":{ + "country_code":"NL", + "hemisphere":"N", + "name":"Amsterdam", + "timezone":"-1" + }, + "moscow": { + "country_code": "RU", + "hemisphere": "N", + "name": "Moscow", + "timezone": "+3" + }, + "manila": { + "country_code": "PH", + "hemisphere": "N", + "name": "Manila", + "timezone": "+8" + } + }, + "openvpn_configuration":{ + "auth":"SHA1", + "cipher":"AES-256-CBC", + "keepalive":"10 30", + "tls-cipher":"DHE-RSA-AES128-SHA", + "tun-ipv6":true, + "dev" : "tun", + "sndbuf" : "0", + "rcvbuf" : "0", + "nobind" : true, + "persist-key" : true, + "key-direction" : "1", + "verb" : "3" + }, + "serial":2, + "version":3 +}
\ No newline at end of file |