diff options
49 files changed, 1402 insertions, 278 deletions
| @@ -87,3 +87,22 @@ go/lib/bitmask-core-sources.jar  go/lib/bitmask-core.aar  go/lib/bitmask-web-core-sources.jar  go/lib/bitmask-web-core.aar + +lib-bitmask-core-arm64/build +lib-bitmask-core-armv7/build +lib-bitmask-core-web/build +lib-bitmask-core-x86/build +lib-bitmask-core-x86_64/build +lib-bitmask-core/build +lib-bitmask-core-arm64/bitmaskcore_arm64-sources.jar +lib-bitmask-core-arm64/bitmaskcore_arm64.aar +lib-bitmask-core-armv7/bitmaskcore_arm-sources.jar +lib-bitmask-core-armv7/bitmaskcore_arm.aar +lib-bitmask-core-web/bitmaskcore_web-sources.jar +lib-bitmask-core-web/bitmaskcore_web.aar +lib-bitmask-core-x86/bitmaskcore_x86-sources.jar +lib-bitmask-core-x86/bitmaskcore_x86.aar +lib-bitmask-core-x86_64/bitmaskcore_x86_64-sources.jar +lib-bitmask-core-x86_64/bitmaskcore_x86_64.aar +lib-bitmask-core/bitmaskcore-sources.jar +lib-bitmask-core/bitmaskcore.aar diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5cc25a02..286e6d45 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -55,6 +55,7 @@ unit_test:    image: "0xacab.org:4567/leap/bitmask_android/android-ndk:latest"    stage: test    script: +    - ./scripts/prepareForTests.sh      - ./gradlew testCustomProductionFatReleaseUnitTest testNormalProductionFatReleaseUnitTest    artifacts:      paths: diff --git a/app/build.gradle b/app/build.gradle index 5c804d75..3e4f4ee2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -43,6 +43,14 @@ android {      buildConfigField 'boolean', 'allow_manual_gateway_selection', 'true'      // grey out background in EipFragment (main screen) if VPN is not running      buildConfigField 'boolean', 'use_color_filter', 'true' +    // decide if we use obfsvpn or shapeshifter as obfs4 lib +    buildConfigField 'boolean', 'use_obfsvpn', 'true' +    // obfsvpn Debugging config fields to pin and configure a particular proxy +    buildConfigField 'boolean', 'obfsvpn_pinning', 'false' +    buildConfigField "String", "obfsvpn_port", '""' +    buildConfigField "String", "obfsvpn_ip", '""' +    buildConfigField "String", "obfsvpn_cert", '""' +    buildConfigField 'boolean', 'obfsvpn_use_kcp', 'false'      // static update url pointing to the latest stable release apk      buildConfigField "String", "update_apk_url", '"https://dl.bitmask.net/client/android/Bitmask-Android-latest.apk"' @@ -133,6 +141,14 @@ android {        buildConfigField 'boolean', 'allow_manual_gateway_selection', 'true'        // grey out background in EipFragment (main screen) if VPN is not running        buildConfigField 'boolean', 'use_color_filter', 'false' +      // decide if we use obfsvpn or shapeshifter as obfs4 lib +      buildConfigField 'boolean', 'use_obfsvpn', 'true' +      // obfsvpn Debugging config fields to pin and configure a particular proxy +      buildConfigField 'boolean', 'obfsvpn_pinning', 'false' +      buildConfigField "String", "obfsvpn_port", '""' +      buildConfigField "String", "obfsvpn_ip", '""' +      buildConfigField "String", "obfsvpn_cert", '""' +      buildConfigField 'boolean', 'obfsvpn_use_kcp', 'false'        //Build Config Fields for automatic apk update checks diff --git a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java index c010ef54..7dd75432 100644 --- a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java +++ b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java @@ -70,6 +70,8 @@ import se.leap.bitmaskclient.BuildConfig;  import se.leap.bitmaskclient.R;  import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_KCP; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN;  import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PROFILE;  import static se.leap.bitmaskclient.base.utils.ConfigHelper.stringEqual; @@ -191,7 +193,8 @@ public class VpnProfile implements Serializable, Cloneable {      private int mProfileVersion;      public boolean mBlockUnusedAddressFamilies = true;      public String mGatewayIp; -    public boolean mUsePluggableTransports; +    private boolean mUseObfs4; +    private boolean mUseObfs4Kcp;      public VpnProfile(String name, Connection.TransportType transportType) {          mUuid = UUID.randomUUID(); @@ -200,7 +203,8 @@ public class VpnProfile implements Serializable, Cloneable {          mConnections = new Connection[1];          mLastUsed = System.currentTimeMillis(); -        mUsePluggableTransports = transportType == OBFS4; +        mUseObfs4 = transportType == OBFS4; +        mUseObfs4Kcp = transportType == OBFS4_KCP;      }      public static String openVpnEscape(String unescaped) { @@ -266,7 +270,8 @@ public class VpnProfile implements Serializable, Cloneable {          if (obj instanceof VpnProfile) {              VpnProfile vp = (VpnProfile) obj;              return stringEqual(vp.mGatewayIp, mGatewayIp) && -                    vp.mUsePluggableTransports == mUsePluggableTransports; +                    vp.mUseObfs4 == mUseObfs4 && +                    vp.mUseObfs4Kcp == mUseObfs4Kcp;          }          return false;      } @@ -296,6 +301,20 @@ public class VpnProfile implements Serializable, Cloneable {          mUuid = uuid;      } +    public boolean usePluggableTransports() { +        return mUseObfs4Kcp || mUseObfs4; +    } + +    public Connection.TransportType getTransportType() { +        if (mUseObfs4) { +            return OBFS4; +        } else if (mUseObfs4Kcp) { +            return OBFS4_KCP; +        } else { +            return OPENVPN; +        } +    } +      public String getName() {          if (TextUtils.isEmpty(mName))              return "No profile name"; @@ -478,7 +497,7 @@ public class VpnProfile implements Serializable, Cloneable {              cfg.append(insertFileData("crl-verify", mCrlFilename));          // compression does not work in conjunction with shapeshifter-dispatcher so far -        if (mUseLzo && !mUsePluggableTransports) { +        if (mUseLzo && !usePluggableTransports()) {              cfg.append("comp-lzo\n");          } diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java index d624af80..4b394136 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -5,6 +5,12 @@  package de.blinkt.openvpn.core; +import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTED; +import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT; +import static de.blinkt.openvpn.core.NetworkSpace.IpAddress; +import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PROFILE; +import static se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper.useObfsVpn; +  import android.Manifest.permission;  import android.annotation.TargetApi;  import android.app.Notification; @@ -47,13 +53,9 @@ import se.leap.bitmaskclient.R;  import se.leap.bitmaskclient.eip.EipStatus;  import se.leap.bitmaskclient.eip.VpnNotificationManager;  import se.leap.bitmaskclient.firewall.FirewallManager; +import se.leap.bitmaskclient.pluggableTransports.ObfsVpnClient;  import se.leap.bitmaskclient.pluggableTransports.Shapeshifter; -import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTED; -import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT; -import static de.blinkt.openvpn.core.NetworkSpace.IpAddress; -import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PROFILE; -  public class OpenVPNService extends VpnService implements StateListener, Callback, ByteCountListener, IOpenVPNServiceInternal, VpnNotificationManager.VpnServiceCallback {      public static final String TAG = OpenVPNService.class.getSimpleName(); @@ -89,6 +91,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac      private Runnable mOpenVPNThread;      private VpnNotificationManager notificationManager;      private Shapeshifter shapeshifter; +    private ObfsVpnClient obfsVpnClient;      private FirewallManager firewallManager;      private final IBinder mBinder = new IOpenVPNServiceInternal.Stub() { @@ -241,6 +244,9 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac                      if (shapeshifter != null) {                          shapeshifter.stop();                          shapeshifter = null; +                    } else if (obfsVpnClient != null && obfsVpnClient.isStarted()) { +                        obfsVpnClient.stop(); +                        obfsVpnClient = null;                      }                      VpnStatus.updateStateString("NOPROCESS", "VPN STOPPED", R.string.state_noprocess, ConnectionStatus.LEVEL_NOTCONNECTED);                  } @@ -311,7 +317,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac          VpnStatus.updateStateString("VPN_GENERATE_CONFIG", "", R.string.building_configration, ConnectionStatus.LEVEL_START);          notificationManager.buildOpenVpnNotification(                  mProfile != null ? mProfile.mName : "", -                mProfile != null && mProfile.mUsePluggableTransports, +                mProfile != null && mProfile.usePluggableTransports(),                  VpnStatus.getLastCleanLogMessage(this),                  VpnStatus.getLastCleanLogMessage(this),                  ConnectionStatus.LEVEL_START, @@ -410,9 +416,16 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac          // An old running VPN should now be exited          mStarting = false; -        if (mProfile.mUsePluggableTransports && connection instanceof Obfs4Connection) { +        if (mProfile.usePluggableTransports() && connection instanceof Obfs4Connection) {              Obfs4Connection obfs4Connection = (Obfs4Connection) connection; -            if (shapeshifter == null) { +            if (useObfsVpn()) { +                if (obfsVpnClient != null && obfsVpnClient.isStarted()) { +                    obfsVpnClient.stop(); +                } +                obfsVpnClient = new ObfsVpnClient(obfs4Connection.getDispatcherOptions()); +                int runningSocksPort = obfsVpnClient.start(); +                connection.setProxyPort(String.valueOf(runningSocksPort)); +            } else if (shapeshifter == null) {                  shapeshifter = new Shapeshifter(obfs4Connection.getDispatcherOptions());                  shapeshifter.start();              } @@ -474,6 +487,10 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac                      Log.d(TAG, "-> stop shapeshifter");                      shapeshifter.stop();                      shapeshifter = null; +                } else if (obfsVpnClient != null && obfsVpnClient.isStarted()) { +                    Log.d(TAG, "-> stop obfsvpnClient"); +                    obfsVpnClient.stop(); +                    obfsVpnClient = null;                  }                  try {                      Thread.sleep(1000); @@ -1016,7 +1033,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac          notificationManager.buildOpenVpnNotification(                  mProfile != null ? mProfile.mName : "", -                mProfile != null && mProfile.mUsePluggableTransports, +                mProfile != null && mProfile.usePluggableTransports(),                  VpnStatus.getLastCleanLogMessage(this),                  VpnStatus.getLastCleanLogMessage(this),                  level, @@ -1047,7 +1064,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac                      humanReadableByteCount(diffOut / OpenVPNManagement.mBytecountInterval, true, getResources()));              notificationManager.buildOpenVpnNotification(                      mProfile != null ? mProfile.mName : "", -                    mProfile != null && mProfile.mUsePluggableTransports, +                    mProfile != null && mProfile.usePluggableTransports(),                      netstat,                      null,                      LEVEL_CONNECTED, @@ -1091,7 +1108,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac          VpnStatus.updateStateString("NEED", "need " + needed, resid, LEVEL_WAITING_FOR_USER_INPUT);          notificationManager.buildOpenVpnNotification(                  mProfile != null ? mProfile.mName : "", -                mProfile != null && mProfile.mUsePluggableTransports, +                mProfile != null && mProfile.usePluggableTransports(),                  getString(resid),                  getString(resid),                  LEVEL_WAITING_FOR_USER_INPUT, diff --git a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java index 4fa5a7b6..f28651f3 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java +++ b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java @@ -543,6 +543,6 @@ public class VpnStatus {      }      public static boolean isUsingBridges() { -        return lastConnectedProfile != null && lastConnectedProfile.mUsePluggableTransports; +        return lastConnectedProfile != null && lastConnectedProfile.usePluggableTransports();      }  } diff --git a/app/src/main/java/de/blinkt/openvpn/core/connection/Connection.java b/app/src/main/java/de/blinkt/openvpn/core/connection/Connection.java index 4cb9c0c7..f60e7333 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/connection/Connection.java +++ b/app/src/main/java/de/blinkt/openvpn/core/connection/Connection.java @@ -39,7 +39,10 @@ public abstract class Connection implements Serializable, Cloneable {      public enum TransportType {          OBFS4("obfs4"), -        OPENVPN("openvpn"); +        OPENVPN("openvpn"), +        OBFS4_KCP("obfs4-1"), + +        PT("metaTransport");          String transport; @@ -51,6 +54,17 @@ public abstract class Connection implements Serializable, Cloneable {          public String toString() {              return transport;          } + +        public boolean isPluggableTransport() { +            return this == OBFS4 || this == OBFS4_KCP || this == PT; +        } + +        public TransportType getMetaType() { +            if (this == OBFS4 || this == OBFS4_KCP || this == PT) { +                return PT; +            } +            return OPENVPN; +        }      } diff --git a/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4Connection.java b/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4Connection.java index 82a7a6aa..5189afcc 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4Connection.java +++ b/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4Connection.java @@ -1,10 +1,12 @@  package de.blinkt.openvpn.core.connection; -import se.leap.bitmaskclient.pluggableTransports.Obfs4Options; - +import static se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper.useObfsVpn;  import static se.leap.bitmaskclient.pluggableTransports.Shapeshifter.DISPATCHER_IP;  import static se.leap.bitmaskclient.pluggableTransports.Shapeshifter.DISPATCHER_PORT; +import se.leap.bitmaskclient.pluggableTransports.Obfs4Options; +import se.leap.bitmaskclient.pluggableTransports.ObfsVpnClient; +  /**   * Created by cyberta on 08.03.19. @@ -16,14 +18,24 @@ public class Obfs4Connection extends Connection {      private Obfs4Options options;      public Obfs4Connection(Obfs4Options options) { +        if (useObfsVpn()) { +            setServerName(options.remoteIP); +            setServerPort(options.remotePort); +            setProxyName(ObfsVpnClient.SOCKS_IP); +            setProxyPort(String.valueOf(ObfsVpnClient.SOCKS_PORT.get())); +            setProxyType(ProxyType.SOCKS5); +        } else { +            setServerName(DISPATCHER_IP); +            setServerPort(DISPATCHER_PORT); +            setProxyName(""); +            setProxyPort(""); +            setProxyType(ProxyType.NONE); +        } +        // while udp/kcp might be used on the wire, +        // we don't use udp for openvpn in case of a obfs4 connection          setUseUdp(false); -        setServerName(DISPATCHER_IP); -        setServerPort(DISPATCHER_PORT); -        setProxyName(""); -        setProxyPort("");          setProxyAuthUser(null);          setProxyAuthPassword(null); -        setProxyType(ProxyType.NONE);          setUseProxyAuth(false);          this.options = options;      } diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java index f2c3b2d6..a2bfff7c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java @@ -51,10 +51,10 @@ import se.leap.bitmaskclient.eip.GatewaysManager;  import static android.content.Context.MODE_PRIVATE;  import static android.view.View.GONE; -import static android.view.View.INVISIBLE;  import static android.view.View.VISIBLE;  import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4;  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.MainActivity.ACTION_SHOW_VPN_FRAGMENT;  import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;  import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES; @@ -92,7 +92,7 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca          eipStatus = EipStatus.getInstance();          eipStatus.addObserver(this);          preferences = getContext().getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); -        selectedTransport = getUseBridges(preferences) ? OBFS4 : OPENVPN; +        selectedTransport = getUseBridges(preferences) ? PT : OPENVPN;          preferences.registerOnSharedPreferenceChangeListener(this);      } @@ -211,7 +211,7 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca      public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {          if (key.equals(USE_BRIDGES)) {              boolean showBridges = getUseBridges(sharedPreferences); -            selectedTransport = showBridges ? OBFS4 : OPENVPN; +            selectedTransport = showBridges ? PT : OPENVPN;              gatewaysManager.updateTransport(selectedTransport);              locationListAdapter.updateTransport(selectedTransport, gatewaysManager);              bridgesHint.setVisibility(showBridges ? VISIBLE : GONE); diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java index 1c859d65..c5593bf7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java @@ -32,7 +32,6 @@ import android.view.View;  import android.view.ViewGroup;  import androidx.annotation.NonNull; -import androidx.annotation.StringRes;  import androidx.appcompat.app.ActionBar;  import androidx.appcompat.app.ActionBarDrawerToggle;  import androidx.appcompat.app.AlertDialog; 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/Constants.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java index bde909ba..b34a31eb 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java @@ -44,6 +44,7 @@ public interface Constants {      String USE_SNOWFLAKE = "use_snowflake";      String PREFER_UDP = "prefer_UDP";      String GATEWAY_PINNING = "gateway_pinning"; +    String ALLOW_EXPERIMENTAL_TRANSPORTS = "allow_experimental_transports";       ////////////////////////////////////////////// @@ -163,6 +164,7 @@ public interface Constants {      String IP_ADDRESS = "ip_address";      String IP_ADDRESS6 = "ip_address6";      String REMOTE = "remote"; +    String SOCKS_PROXY = "socks-proxy";      String PORTS = "ports";      String PROTOCOLS = "protocols";      String UDP = "udp"; diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Location.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Location.java index 064f25c0..26f6b14a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/models/Location.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Location.java @@ -21,10 +21,7 @@ import androidx.annotation.NonNull;  import java.util.Comparator;  import java.util.HashMap; -import java.util.HashSet; -import java.util.function.ToDoubleFunction; -import de.blinkt.openvpn.core.connection.Connection;  import de.blinkt.openvpn.core.connection.Connection.TransportType;  public class Location implements Cloneable { @@ -50,27 +47,27 @@ public class Location implements Cloneable {      }      public boolean supportsTransport(TransportType transportType) { -        return numberOfGateways.containsKey(transportType); +        return numberOfGateways.containsKey(transportType.getMetaType());      }      public void setAverageLoad(TransportType transportType, double load) { -        averageLoad.put(transportType, load); +        averageLoad.put(transportType.getMetaType(), load);      }      public double getAverageLoad(TransportType transportType) { -        if (averageLoad.containsKey(transportType)) { -            return averageLoad.get(transportType); +        if (averageLoad.containsKey(transportType.getMetaType())) { +            return averageLoad.get(transportType.getMetaType());          }          return 0;      }      public void setNumberOfGateways(TransportType transportType, int numbers) { -        numberOfGateways.put(transportType, numbers); +        numberOfGateways.put(transportType.getMetaType(), numbers);      }      public int getNumberOfGateways(TransportType transportType) { -        if (numberOfGateways.containsKey(transportType)) { -            return numberOfGateways.get(transportType); +        if (numberOfGateways.containsKey(transportType.getMetaType())) { +            return numberOfGateways.get(transportType.getMetaType());          }          return 0;      } @@ -112,7 +109,7 @@ public class Location implements Cloneable {      public static class SortByAverageLoad implements Comparator<Location> {          TransportType transportType;          public SortByAverageLoad(TransportType transportType) { -            this.transportType = transportType; +            this.transportType = transportType.getMetaType();          }          @Override diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Pair.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Pair.java new file mode 100644 index 00000000..e2ef4622 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Pair.java @@ -0,0 +1,57 @@ +package se.leap.bitmaskclient.base.models; + +import java.util.Objects; + +/** + * Container to ease passing around a tuple of two objects. This object provides a sensible + * implementation of equals(), returning true if equals() is true on each of the contained + * objects. + */ +public class Pair<F, S> { +    public final F first; +    public final S second; + +    /** +     * Constructor for a Pair. +     * +     * @param first the first object in the Pair +     * @param second the second object in the pair +     */ +    public Pair(F first, S second) { +        this.first = first; +        this.second = second; +    } + +    /** +     * Checks the two objects for equality by delegating to their respective +     * {@link Object#equals(Object)} methods. +     * +     * @param o the {@link Pair} to which this one is to be checked for equality +     * @return true if the underlying objects of the Pair are both considered +     *         equal +     */ +    @Override +    public boolean equals(Object o) { +        if (!(o instanceof Pair)) { +            return false; +        } +        Pair<?, ?> p = (Pair<?, ?>) o; +        return Objects.equals(p.first, first) && Objects.equals(p.second, second); +    } + +    /** +     * Compute a hash code using the hash codes of the underlying objects +     * +     * @return a hashcode of the Pair +     */ +    @Override +    public int hashCode() { +        return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode()); +    } + +    @Override +    public String toString() { +        return "Pair{" + String.valueOf(first) + " " + String.valueOf(second) + "}"; +    } + +} 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/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java index ca1261a8..8ac5baf0 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java @@ -274,4 +274,11 @@ public class ConfigHelper {          return matcher.matches();      } +    // ObfsVpnHelper class allows us to mock BuildConfig.use_obfsvpn while +    // not mocking the whole ConfigHelper class +    public static class ObfsVpnHelper { +        public static boolean useObfsVpn() { +            return BuildConfig.use_obfsvpn; +        } +    }  } diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java index 08bfbdc3..3a2cf754 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java @@ -1,25 +1,7 @@  package se.leap.bitmaskclient.base.utils; -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.Preference; - -import androidx.annotation.NonNull; -import androidx.annotation.WorkerThread; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.HashSet; -import java.util.Set; - -import de.blinkt.openvpn.VpnProfile; -import se.leap.bitmaskclient.base.models.Provider; -import se.leap.bitmaskclient.tor.TorStatusObservable; -  import static android.content.Context.MODE_PRIVATE; +import static se.leap.bitmaskclient.base.models.Constants.ALLOW_EXPERIMENTAL_TRANSPORTS;  import static se.leap.bitmaskclient.base.models.Constants.ALLOW_TETHERING_BLUETOOTH;  import static se.leap.bitmaskclient.base.models.Constants.ALLOW_TETHERING_USB;  import static se.leap.bitmaskclient.base.models.Constants.ALLOW_TETHERING_WIFI; @@ -42,6 +24,24 @@ 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.models.Constants.USE_SNOWFLAKE; +import android.content.Context; +import android.content.SharedPreferences; + +import androidx.annotation.NonNull; +import androidx.annotation.WorkerThread; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashSet; +import java.util.Set; + +import de.blinkt.openvpn.VpnProfile; +import se.leap.bitmaskclient.base.models.Provider; +import se.leap.bitmaskclient.tor.TorStatusObservable; +  /**   * Created by cyberta on 18.03.18.   */ @@ -238,6 +238,14 @@ public class PreferenceHelper {          return getBoolean(context, SHOW_EXPERIMENTAL, false);      } +    public static void setAllowExperimentalTransports(Context context, boolean show) { +        putBoolean(context, ALLOW_EXPERIMENTAL_TRANSPORTS, show); +    } + +    public static boolean allowExperimentalTransports(Context context) { +        return getBoolean(context, ALLOW_EXPERIMENTAL_TRANSPORTS, false); +    } +      public static void setUseIPv6Firewall(Context context, boolean useFirewall) {          putBoolean(context, USE_IPv6_FIREWALL, useFirewall);      } diff --git a/app/src/main/java/se/leap/bitmaskclient/base/views/SelectLocationEntry.java b/app/src/main/java/se/leap/bitmaskclient/base/views/SelectLocationEntry.java index 3d4f93ff..554fe958 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/views/SelectLocationEntry.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/views/SelectLocationEntry.java @@ -6,7 +6,6 @@ import android.util.AttributeSet;  import android.view.LayoutInflater;  import android.view.View;  import android.widget.LinearLayout; -import android.widget.RelativeLayout;  import androidx.appcompat.widget.AppCompatImageView;  import androidx.appcompat.widget.AppCompatTextView; @@ -71,7 +70,7 @@ public class SelectLocationEntry extends LinearLayout {          boolean supportsSelectedTransport = location.supportsTransport(transportType);          locationText.setVisibility(hasData ? VISIBLE : GONE);          locationIndicator.setVisibility(hasData ? VISIBLE : GONE); -        bridgesView.setVisibility(transportType == OBFS4 && supportsSelectedTransport ? VISIBLE : GONE); +        bridgesView.setVisibility(transportType.isPluggableTransport()  && supportsSelectedTransport ? VISIBLE : GONE);          locationText.setText(location.getName());          locationIndicator.setLoad(Load.getLoadByValue(location.getAverageLoad(transportType)));          selectedView.setChecked(location.selected); diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java index 46f91781..b3efd21f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -16,6 +16,36 @@   */  package se.leap.bitmaskclient.eip; +import static android.app.Activity.RESULT_CANCELED; +import static android.app.Activity.RESULT_OK; +import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid; +import static se.leap.bitmaskclient.R.string.warning_client_parsing_error_gateways; +import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT; +import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY; +import static se.leap.bitmaskclient.base.models.Constants.CLEARLOG; +import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; +import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_IS_RUNNING; +import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_LAUNCH_VPN; +import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START; +import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START_ALWAYS_ON_VPN; +import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START_BLOCKING_VPN; +import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_STOP; +import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_STOP_BLOCKING_VPN; +import static se.leap.bitmaskclient.base.models.Constants.EIP_EARLY_ROUTES; +import static se.leap.bitmaskclient.base.models.Constants.EIP_N_CLOSEST_GATEWAY; +import static se.leap.bitmaskclient.base.models.Constants.EIP_RECEIVER; +import static se.leap.bitmaskclient.base.models.Constants.EIP_RESTART_ON_BOOT; +import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PROFILE; +import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES; +import static se.leap.bitmaskclient.base.utils.ConfigHelper.ensureNotOnMainThread; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferredCity; +import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_INVALID_PROFILE; +import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_INVALID_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_VPN_PREPARE; +import static se.leap.bitmaskclient.eip.EIP.EIPErrors.NO_MORE_GATEWAYS; +import static se.leap.bitmaskclient.eip.EipResultBroadcast.tellToReceiverOrBroadcast; +  import android.annotation.SuppressLint;  import android.content.ComponentName;  import android.content.Context; @@ -31,6 +61,7 @@ import android.os.ResultReceiver;  import android.util.Log;  import androidx.annotation.NonNull; +import androidx.annotation.Nullable;  import androidx.annotation.StringRes;  import androidx.annotation.WorkerThread;  import androidx.core.app.JobIntentService; @@ -57,42 +88,10 @@ import de.blinkt.openvpn.core.connection.Connection;  import se.leap.bitmaskclient.R;  import se.leap.bitmaskclient.base.OnBootReceiver;  import se.leap.bitmaskclient.base.models.Provider; +import se.leap.bitmaskclient.base.models.Pair;  import se.leap.bitmaskclient.base.models.ProviderObservable;  import se.leap.bitmaskclient.base.utils.PreferenceHelper; -import static android.app.Activity.RESULT_CANCELED; -import static android.app.Activity.RESULT_OK; -import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; -import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN; -import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid; -import static se.leap.bitmaskclient.R.string.warning_client_parsing_error_gateways; -import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT; -import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY; -import static se.leap.bitmaskclient.base.models.Constants.CLEARLOG; -import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; -import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_IS_RUNNING; -import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_LAUNCH_VPN; -import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START; -import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START_ALWAYS_ON_VPN; -import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START_BLOCKING_VPN; -import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_STOP; -import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_STOP_BLOCKING_VPN; -import static se.leap.bitmaskclient.base.models.Constants.EIP_EARLY_ROUTES; -import static se.leap.bitmaskclient.base.models.Constants.EIP_N_CLOSEST_GATEWAY; -import static se.leap.bitmaskclient.base.models.Constants.EIP_RECEIVER; -import static se.leap.bitmaskclient.base.models.Constants.EIP_RESTART_ON_BOOT; -import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PROFILE; -import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE; -import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES; -import static se.leap.bitmaskclient.base.utils.ConfigHelper.ensureNotOnMainThread; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferredCity; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseBridges; -import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_INVALID_PROFILE; -import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_INVALID_VPN_CERTIFICATE; -import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_VPN_PREPARE; -import static se.leap.bitmaskclient.eip.EIP.EIPErrors.NO_MORE_GATEWAYS; -import static se.leap.bitmaskclient.eip.EipResultBroadcast.tellToReceiverOrBroadcast; -  /**   * EIP is the abstract base class for interacting with and managing the Encrypted   * Internet Proxy connection.  Connections are started, stopped, and queried through @@ -256,8 +255,8 @@ public final class EIP extends JobIntentService implements Observer {              return;          } -        Gateway gateway = gatewaysManager.select(nClosestGateway); -        launchActiveGateway(gateway, nClosestGateway, result); +        Pair<Gateway, Connection.TransportType> gatewayTransportTypePair = gatewaysManager.select(nClosestGateway); +        launchActiveGateway(gatewayTransportTypePair, nClosestGateway, result);          if (result.containsKey(BROADCAST_RESULT_KEY) && !result.getBoolean(BROADCAST_RESULT_KEY)) {              tellToReceiverOrBroadcast(this, EIP_ACTION_START, RESULT_CANCELED, result);          } else { @@ -271,7 +270,7 @@ public final class EIP extends JobIntentService implements Observer {       */      private void startEIPAlwaysOnVpn() {          GatewaysManager gatewaysManager = new GatewaysManager(getApplicationContext()); -        Gateway gateway = gatewaysManager.select(0); +        Pair<Gateway, Connection.TransportType> gatewayTransportTypePair = gatewaysManager.select(0);          Bundle result = new Bundle();          if (shouldUpdateVPNCertificate()) { @@ -280,7 +279,7 @@ public final class EIP extends JobIntentService implements Observer {              ProviderObservable.getInstance().updateProvider(p);          } -        launchActiveGateway(gateway, 0, result); +        launchActiveGateway(gatewayTransportTypePair, 0, result);          if (result.containsKey(BROADCAST_RESULT_KEY) && !result.getBoolean(BROADCAST_RESULT_KEY)){              VpnStatus.logWarning("ALWAYS-ON VPN: " + getString(R.string.no_vpn_profiles_defined));          } @@ -324,13 +323,13 @@ public final class EIP extends JobIntentService implements Observer {      /**       * starts the VPN and connects to the given gateway       * -     * @param gateway to connect to +     * @param gatewayTransportTypePair Pair of Gateway and associated transport used to connect       */ -    private void launchActiveGateway(Gateway gateway, int nClosestGateway, Bundle result) { +    private void launchActiveGateway(@Nullable Pair<Gateway, Connection.TransportType> gatewayTransportTypePair, int nClosestGateway, Bundle result) {          VpnProfile profile; -        Connection.TransportType transportType = getUseBridges(this) ? OBFS4 : OPENVPN; -        if (gateway == null || -                (profile = gateway.getProfile(transportType)) == null) { + +        if (gatewayTransportTypePair == null || gatewayTransportTypePair.first == null || +                (profile = gatewayTransportTypePair.first.getProfile(gatewayTransportTypePair.second)) == null) {              String preferredLocation = getPreferredCity(getApplicationContext());              if (preferredLocation != null) {                  setErrorResult(result, NO_MORE_GATEWAYS.toString(), getStringResourceForNoMoreGateways(), getString(R.string.app_name), preferredLocation); diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java index 60507363..e9281609 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java @@ -16,6 +16,17 @@   */  package se.leap.bitmaskclient.eip; +import static se.leap.bitmaskclient.base.models.Constants.FULLNESS; +import static se.leap.bitmaskclient.base.models.Constants.HOST; +import static se.leap.bitmaskclient.base.models.Constants.IP_ADDRESS; +import static se.leap.bitmaskclient.base.models.Constants.LOCATION; +import static se.leap.bitmaskclient.base.models.Constants.LOCATIONS; +import static se.leap.bitmaskclient.base.models.Constants.NAME; +import static se.leap.bitmaskclient.base.models.Constants.OPENVPN_CONFIGURATION; +import static se.leap.bitmaskclient.base.models.Constants.OVERLOAD; +import static se.leap.bitmaskclient.base.models.Constants.TIMEZONE; +import static se.leap.bitmaskclient.base.models.Constants.VERSION; +  import android.content.Context;  import androidx.annotation.NonNull; @@ -36,17 +47,6 @@ import de.blinkt.openvpn.core.connection.Connection;  import se.leap.bitmaskclient.base.utils.ConfigHelper;  import se.leap.bitmaskclient.base.utils.PreferenceHelper; -import static se.leap.bitmaskclient.base.models.Constants.FULLNESS; -import static se.leap.bitmaskclient.base.models.Constants.HOST; -import static se.leap.bitmaskclient.base.models.Constants.IP_ADDRESS; -import static se.leap.bitmaskclient.base.models.Constants.LOCATION; -import static se.leap.bitmaskclient.base.models.Constants.LOCATIONS; -import static se.leap.bitmaskclient.base.models.Constants.NAME; -import static se.leap.bitmaskclient.base.models.Constants.OPENVPN_CONFIGURATION; -import static se.leap.bitmaskclient.base.models.Constants.OVERLOAD; -import static se.leap.bitmaskclient.base.models.Constants.TIMEZONE; -import static se.leap.bitmaskclient.base.models.Constants.VERSION; -  /**   * Gateway provides objects defining gateways and their metadata.   * Each instance contains a VpnProfile for OpenVPN specific data and member @@ -175,7 +175,8 @@ public class Gateway {      private @NonNull HashMap<Connection.TransportType, VpnProfile> createVPNProfiles(Context context)              throws ConfigParser.ConfigParseError, IOException, JSONException {          boolean preferUDP = PreferenceHelper.getPreferUDP(context); -        VpnConfigGenerator vpnConfigurationGenerator = new VpnConfigGenerator(generalConfiguration, secrets, gateway, apiVersion, preferUDP); +        boolean allowExperimentalTransports = PreferenceHelper.allowExperimentalTransports(context); +        VpnConfigGenerator vpnConfigurationGenerator = new VpnConfigGenerator(generalConfiguration, secrets, gateway, apiVersion, preferUDP, allowExperimentalTransports);          HashMap<Connection.TransportType, VpnProfile> profiles = vpnConfigurationGenerator.generateVpnProfiles();          addProfileInfos(context, profiles);          return profiles; @@ -194,6 +195,9 @@ public class Gateway {      }      public boolean supportsTransport(Connection.TransportType transportType) { +        if (transportType == Connection.TransportType.PT) { +            return supportsPluggableTransports(); +        }          return vpnProfiles.get(transportType) != null;      } @@ -201,6 +205,15 @@ public class Gateway {          return new HashSet<>(vpnProfiles.keySet());      } +    public boolean supportsPluggableTransports() { +        for (Connection.TransportType transportType : vpnProfiles.keySet()) { +            if (transportType.isPluggableTransport() && vpnProfiles.get(transportType) != null) { +                return true; +            } +        } +        return false; +    } +      public int getTimezone() {          return timezone;      } 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 a11c7e34..db7dd38c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -43,12 +43,15 @@ import de.blinkt.openvpn.core.connection.Connection;  import de.blinkt.openvpn.core.connection.Connection.TransportType;  import se.leap.bitmaskclient.BuildConfig;  import se.leap.bitmaskclient.base.models.Location; +import se.leap.bitmaskclient.base.models.Pair;  import se.leap.bitmaskclient.base.models.Provider;  import se.leap.bitmaskclient.base.models.ProviderObservable;  import se.leap.bitmaskclient.base.utils.PreferenceHelper;  import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_KCP;  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;  import static se.leap.bitmaskclient.base.models.Constants.HOST;  import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PRIVATE_KEY; @@ -111,18 +114,18 @@ public class GatewaysManager {       * select closest Gateway        * @return the n closest Gateway       */ -    public Gateway select(int nClosest) { +    public Pair<Gateway, TransportType> select(int nClosest) {          String selectedCity = getPreferredCity(context);          return select(nClosest, selectedCity);      } -    public Gateway select(int nClosest, String city) { -        TransportType transportType = getUseBridges(context) ? OBFS4 : OPENVPN; +    public Pair<Gateway, TransportType> select(int nClosest, String city) { +        TransportType[] transportTypes = getUseBridges(context) ? new TransportType[]{OBFS4, OBFS4_KCP} : new TransportType[]{OPENVPN};          if (presortedList.size() > 0) { -            return getGatewayFromPresortedList(nClosest, transportType, city); +            return getGatewayFromPresortedList(nClosest, transportTypes, city);          } -        return getGatewayFromTimezoneCalculation(nClosest, transportType, city); +        return getGatewayFromTimezoneCalculation(nClosest, transportTypes, city);      }      public void updateTransport(TransportType transportType) { @@ -158,7 +161,7 @@ public class GatewaysManager {              } else {                  int index = locationNames.get(gateway.getName());                  Location location = locations.get(index); -                updateLocation(location, gateway, OBFS4); +                updateLocation(location, gateway, PT);                  updateLocation(location, gateway, OPENVPN);                  locations.set(index, location);              } @@ -173,9 +176,9 @@ public class GatewaysManager {      private Location initLocation(String name, Gateway gateway, String preferredCity) {          HashMap<TransportType, Double> averageLoadMap = new HashMap<>();          HashMap<TransportType, Integer> numberOfGatewaysMap = new HashMap<>(); -        if (gateway.getSupportedTransports().contains(OBFS4)) { -            averageLoadMap.put(OBFS4, gateway.getFullness()); -            numberOfGatewaysMap.put(OBFS4, 1); +        if (gateway.supportsPluggableTransports()) { +            averageLoadMap.put(PT, gateway.getFullness()); +            numberOfGatewaysMap.put(PT, 1);          }          if (gateway.getSupportedTransports().contains(OPENVPN)) {              averageLoadMap.put(OPENVPN, gateway.getFullness()); @@ -189,7 +192,7 @@ public class GatewaysManager {      }      private void updateLocation(Location location, Gateway gateway, Connection.TransportType transportType) { -        if (gateway.getSupportedTransports().contains(transportType)) { +        if (gateway.supportsTransport(transportType)) {              double averageLoad = location.getAverageLoad(transportType);              int numberOfGateways = location.getNumberOfGateways(transportType);              averageLoad = (numberOfGateways * averageLoad + gateway.getFullness()) / (numberOfGateways + 1); @@ -218,35 +221,40 @@ public class GatewaysManager {          return Load.getLoadByValue(location.getAverageLoad(transportType));      } -    private Gateway getGatewayFromTimezoneCalculation(int nClosest, TransportType transportType, @Nullable String city) { +    private Pair<Gateway, TransportType> getGatewayFromTimezoneCalculation(int nClosest, TransportType[] transportTypes, @Nullable String city) {          List<Gateway> list = new ArrayList<>(gateways.values());          GatewaySelector gatewaySelector = new GatewaySelector(list);          Gateway gateway;          int found  = 0;          int i = 0;          while ((gateway = gatewaySelector.select(i)) != null) { -            if ((city == null && gateway.supportsTransport(transportType)) || -                    (gateway.getName().equals(city) && gateway.supportsTransport(transportType))) { -                if (found == nClosest) { -                    return gateway; +            for (TransportType transportType : transportTypes) { +                if ((city == null && gateway.supportsTransport(transportType)) || +                        (gateway.getName().equals(city) && gateway.supportsTransport(transportType))) { +                    if (found == nClosest) { +                        return new Pair<>(gateway, transportType); +                    } +                    found++;                  } -                found++;              }              i++;          }          return null;      } -    private Gateway getGatewayFromPresortedList(int nClosest, TransportType transportType, @Nullable String city) { +    private Pair<Gateway, TransportType> getGatewayFromPresortedList(int nClosest, TransportType[] transportTypes, @Nullable String city) {          int found = 0;          for (Gateway gateway : presortedList) { -            if ((city == null && gateway.supportsTransport(transportType)) || -                    (gateway.getName().equals(city) && gateway.supportsTransport(transportType))) { -                if (found == nClosest) { -                    return gateway; +            for (TransportType transportType : transportTypes) { +                if ((city == null && gateway.supportsTransport(transportType)) || +                        (gateway.getName().equals(city) && gateway.supportsTransport(transportType))) { +                    if (found == nClosest) { +                        return new Pair<>(gateway, transportType); +                    } +                    found++;                  } -                found++;              } +          }          return null;      } @@ -265,7 +273,7 @@ public class GatewaysManager {      }      private int getPositionFromPresortedList(VpnProfile profile) { -        TransportType transportType = profile.mUsePluggableTransports ? OBFS4 : OPENVPN; +        TransportType transportType = profile.getTransportType();          int nClosest = 0;          for (Gateway gateway : presortedList) {              if (gateway.supportsTransport(transportType)) { @@ -277,9 +285,9 @@ public class GatewaysManager {          }          return -1;      } -     +      private int getPositionFromTimezoneCalculatedList(VpnProfile profile) { -        TransportType transportType = profile.mUsePluggableTransports ? OBFS4 : OPENVPN; +        TransportType transportType = profile.getTransportType();          GatewaySelector gatewaySelector = new GatewaySelector(new ArrayList<>(gateways.values()));          Gateway gateway;          int nClosest = 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 5ddb74ab..58732713 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java @@ -16,6 +16,25 @@   */  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_KCP; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN; +import static se.leap.bitmaskclient.base.models.Constants.CAPABILITIES; +import static se.leap.bitmaskclient.base.models.Constants.IP_ADDRESS; +import static se.leap.bitmaskclient.base.models.Constants.IP_ADDRESS6; +import static se.leap.bitmaskclient.base.models.Constants.OPTIONS; +import static se.leap.bitmaskclient.base.models.Constants.PORTS; +import static se.leap.bitmaskclient.base.models.Constants.PROTOCOLS; +import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PRIVATE_KEY; +import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.base.models.Constants.REMOTE; +import static se.leap.bitmaskclient.base.models.Constants.TRANSPORT; +import static se.leap.bitmaskclient.base.models.Constants.TYPE; +import static se.leap.bitmaskclient.base.models.Constants.UDP; +import static se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper.useObfsVpn; +import static se.leap.bitmaskclient.pluggableTransports.Shapeshifter.DISPATCHER_IP; +import static se.leap.bitmaskclient.pluggableTransports.Shapeshifter.DISPATCHER_PORT; +  import androidx.annotation.VisibleForTesting;  import org.json.JSONArray; @@ -31,50 +50,36 @@ import de.blinkt.openvpn.VpnProfile;  import de.blinkt.openvpn.core.ConfigParser;  import de.blinkt.openvpn.core.VpnStatus;  import de.blinkt.openvpn.core.connection.Connection; +import se.leap.bitmaskclient.BuildConfig;  import se.leap.bitmaskclient.base.models.Provider;  import se.leap.bitmaskclient.base.utils.ConfigHelper;  import se.leap.bitmaskclient.pluggableTransports.Obfs4Options; -import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; -import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN; -import static se.leap.bitmaskclient.base.models.Constants.CAPABILITIES; -import static se.leap.bitmaskclient.base.models.Constants.IP_ADDRESS; -import static se.leap.bitmaskclient.base.models.Constants.IP_ADDRESS6; -import static se.leap.bitmaskclient.base.models.Constants.OPTIONS; -import static se.leap.bitmaskclient.base.models.Constants.PORTS; -import static se.leap.bitmaskclient.base.models.Constants.PROTOCOLS; -import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PRIVATE_KEY; -import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE; -import static se.leap.bitmaskclient.base.models.Constants.REMOTE; -import static se.leap.bitmaskclient.base.models.Constants.TRANSPORT; -import static se.leap.bitmaskclient.base.models.Constants.TYPE; -import static se.leap.bitmaskclient.base.models.Constants.UDP; -import static se.leap.bitmaskclient.pluggableTransports.Shapeshifter.DISPATCHER_IP; -import static se.leap.bitmaskclient.pluggableTransports.Shapeshifter.DISPATCHER_PORT; -  public class VpnConfigGenerator { -    private JSONObject generalConfiguration; -    private JSONObject gateway; -    private JSONObject secrets; +    private final JSONObject generalConfiguration; +    private final JSONObject gateway; +    private final JSONObject secrets;      private JSONObject obfs4Transport; -    private int apiVersion; -    private boolean preferUDP; +    private JSONObject obfs4TKcpTransport; +    private final int apiVersion; +    private final boolean preferUDP; +    private final boolean experimentalTransports;      public final static String TAG = VpnConfigGenerator.class.getSimpleName();      private final String newLine = System.getProperty("line.separator"); // Platform new line -    public VpnConfigGenerator(JSONObject generalConfiguration, JSONObject secrets, JSONObject gateway, int apiVersion, boolean preferUDP) throws ConfigParser.ConfigParseError { +    public VpnConfigGenerator(JSONObject generalConfiguration, JSONObject secrets, JSONObject gateway, int apiVersion, boolean preferUDP, boolean experimentalTransports) throws ConfigParser.ConfigParseError {          this.generalConfiguration = generalConfiguration;          this.gateway = gateway;          this.secrets = secrets;          this.apiVersion = apiVersion;          this.preferUDP = preferUDP; +        this.experimentalTransports = experimentalTransports;          checkCapabilities();      }      public void checkCapabilities() throws ConfigParser.ConfigParseError { -          try {              if (apiVersion >= 3) {                  JSONArray supportedTransports = gateway.getJSONObject(CAPABILITIES).getJSONArray(TRANSPORT); @@ -82,7 +87,11 @@ public class VpnConfigGenerator {                      JSONObject transport = supportedTransports.getJSONObject(i);                      if (transport.getString(TYPE).equals(OBFS4.toString())) {                          obfs4Transport = transport; -                        break; +                        if (!experimentalTransports) { +                            break; +                        } +                    } else if (experimentalTransports && transport.getString(TYPE).equals(OBFS4_KCP.toString())) { +                        obfs4TKcpTransport = transport;                      }                  }              } @@ -106,6 +115,13 @@ public class VpnConfigGenerator {                  e.printStackTrace();              }          } +        if (supportsObfs4Kcp()) { +            try { +                profiles.put(OBFS4_KCP, createProfile(OBFS4_KCP)); +            } catch (ConfigParser.ConfigParseError | NumberFormatException | JSONException | IOException e) { +                e.printStackTrace(); +            } +        }          return profiles;      } @@ -113,6 +129,10 @@ public class VpnConfigGenerator {          return obfs4Transport != null;      } +    private boolean supportsObfs4Kcp() { +        return obfs4TKcpTransport != null; +    } +      private String getConfigurationString(Connection.TransportType transportType) {          return generalConfiguration()                  + newLine @@ -129,18 +149,28 @@ public class VpnConfigGenerator {          ConfigParser icsOpenvpnConfigParser = new ConfigParser();          icsOpenvpnConfigParser.parseConfig(new StringReader(configuration));          if (transportType == OBFS4) { -            icsOpenvpnConfigParser.setObfs4Options(getObfs4Options()); +            icsOpenvpnConfigParser.setObfs4Options(getObfs4Options(obfs4Transport, false)); +        } else if (transportType == OBFS4_KCP) { +            icsOpenvpnConfigParser.setObfs4Options(getObfs4Options(obfs4TKcpTransport, true));          }          return icsOpenvpnConfigParser.convertProfile(transportType);      } -    private Obfs4Options getObfs4Options() throws JSONException { -        JSONObject transportOptions = obfs4Transport.getJSONObject(OPTIONS); +    private Obfs4Options getObfs4Options(JSONObject transportJson, boolean useUdp) throws JSONException { +        JSONObject transportOptions = transportJson.getJSONObject(OPTIONS);          String iatMode = transportOptions.getString("iatMode");          String cert = transportOptions.getString("cert");          String port = obfs4Transport.getJSONArray(PORTS).getString(0);          String ip = gateway.getString(IP_ADDRESS); -        return new Obfs4Options(ip, port, cert, iatMode); +        boolean udp = useUdp; + +        if (BuildConfig.obfsvpn_pinning) { +            cert = BuildConfig.obfsvpn_cert; +            port = BuildConfig.obfsvpn_port; +            ip = BuildConfig.obfsvpn_port; +            udp = BuildConfig.obfsvpn_use_kcp; +        } +        return new Obfs4Options(ip, port, cert, iatMode, udp);      }      private String generalConfiguration() { @@ -321,10 +351,27 @@ public class VpnConfigGenerator {              return;          } +        JSONArray ports = obfs4Transport.getJSONArray(PORTS); +        if (ports.isNull(0)){ +            VpnStatus.logError("Misconfigured provider: no ports defined in obfs4 transport JSON."); +            return; +        } +          String route = "route " + ipAddress + " 255.255.255.255 net_gateway" + newLine;          stringBuilder.append(route); -        String remote = REMOTE + " " + DISPATCHER_IP + " " + DISPATCHER_PORT + " tcp" + newLine; -        stringBuilder.append(remote); +        if (useObfsVpn()) { +            String remote; +            if (BuildConfig.obfsvpn_pinning) { +                remote = REMOTE + " " + BuildConfig.obfsvpn_ip + " " + BuildConfig.obfsvpn_port + newLine; +            } else { +                remote = REMOTE + " " + ipAddress + " " + ports.getString(0) + newLine; +            } + +            stringBuilder.append(remote); +        } else { +            String remote = REMOTE + " " + DISPATCHER_IP + " " + DISPATCHER_PORT + " tcp" + newLine; +            stringBuilder.append(remote); +        }      }      private String secretsConfiguration() { diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Obfs4Options.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Obfs4Options.java index 2f9cb732..b96f88ca 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Obfs4Options.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Obfs4Options.java @@ -7,12 +7,15 @@ public class Obfs4Options implements Serializable {      public String iatMode;      public String remoteIP;      public String remotePort; +    // openvpn is still using tcp, obfs4 is wrapped in kcp, if udp == true +    public boolean udp; -    public Obfs4Options(String remoteIP, String remotePort, String cert, String iatMode) { +    public Obfs4Options(String remoteIP, String remotePort, String cert, String iatMode, boolean udp) {          this.cert = cert;          this.iatMode = iatMode;          this.remoteIP = remoteIP;          this.remotePort = remotePort; +        this.udp = udp;      }  } diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java new file mode 100644 index 00000000..f6c8837e --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java @@ -0,0 +1,127 @@ +package se.leap.bitmaskclient.pluggableTransports; + +import android.util.Log; + +import java.util.Observable; +import java.util.Observer; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import client.Client_; +import de.blinkt.openvpn.core.ConnectionStatus; +import de.blinkt.openvpn.core.VpnStatus; +import se.leap.bitmaskclient.eip.EipStatus; + +public class ObfsVpnClient implements Observer, client.EventLogger { + +    public static final AtomicInteger SOCKS_PORT = new AtomicInteger(4430); +    public static final String SOCKS_IP = "127.0.0.1"; +    private static final String ERR_BIND = "bind: address already in use"; + +    private static final String TAG = ObfsVpnClient.class.getSimpleName(); +    private volatile boolean noNetwork; +    private final AtomicBoolean pendingNetworkErrorHandling = new AtomicBoolean(false); +    private final AtomicInteger reconnectRetry = new AtomicInteger(0); +    private static final int MAX_RETRY = 5; + +    private final client.Client_ obfsVpnClient; +    private final Object LOCK = new Object(); + +    public ObfsVpnClient(Obfs4Options options) { +        obfsVpnClient = new Client_(options.udp, SOCKS_IP+":"+SOCKS_PORT.get(), options.cert); +        obfsVpnClient.setEventLogger(this); +    } + +    /** +     * starts the client +     * @return the port ObfsVpn is running on +     */ +    public int start() { +        synchronized (LOCK) { +            Log.d(TAG, "aquired LOCK"); +            new Thread(this::startSync).start(); +            waitUntilStarted(); +            Log.d(TAG, "returning LOCK after " + (reconnectRetry.get() + 1) * 200 +" ms"); +        } +        return SOCKS_PORT.get(); +    } + +    private void waitUntilStarted() { +        int count = -1; +        try { +            while (count < reconnectRetry.get() && reconnectRetry.get() < MAX_RETRY) { +                Thread.sleep(200); +                count++; +            } +        } catch (InterruptedException e) { +            e.printStackTrace(); +        } +    } + +    private void startSync() { +        try { +            obfsVpnClient.start(); +        } catch (Exception e) { +            Log.e(TAG, "[obfsvpn] exception: " + e.getLocalizedMessage()); +            VpnStatus.logError("[obfsvpn] " + e.getLocalizedMessage()); +            if (e.getLocalizedMessage() != null && e.getLocalizedMessage().contains(ERR_BIND) && reconnectRetry.get() < MAX_RETRY) { +                reconnectRetry.addAndGet(1); +                SOCKS_PORT.addAndGet(1); +                obfsVpnClient.setSocksAddr(SOCKS_IP+":"+SOCKS_PORT.get()); +                Log.d(TAG, "[obfsvpn] reconnecting on different port... " + SOCKS_PORT.get()); +                VpnStatus.logDebug("[obfsvpn] reconnecting on different port... " + SOCKS_PORT.get()); +                startSync(); +            } else if (noNetwork) { +                pendingNetworkErrorHandling.set(true); +            } +        } +    } + +    public void stop() { +        synchronized (LOCK) { +            Log.d(TAG, "stopping obfsVpnClient..."); +            try { +                obfsVpnClient.stop(); +                reconnectRetry.set(0); +                SOCKS_PORT.set(4430); +                Thread.sleep(100); +            } catch (Exception e) { +                e.printStackTrace(); +                VpnStatus.logError("[obfsvpn] " + e.getLocalizedMessage()); +            } +            pendingNetworkErrorHandling.set(false); +            Log.d(TAG, "stopping obfsVpnClient releasing LOCK ..."); +        } +    } + +    public boolean isStarted() { +        return obfsVpnClient.isStarted(); +    } + +    @Override +    public void update(Observable observable, Object arg) { +        if (observable instanceof EipStatus) { +            EipStatus status = (EipStatus) observable; +            if (status.getLevel() == ConnectionStatus.LEVEL_NONETWORK) { +                noNetwork = true; +            } else { +                noNetwork = false; +                if (pendingNetworkErrorHandling.getAndSet(false)) { +                    stop(); +                    start(); +                } +            } +        } +    } + +    @Override +    public void error(String s) { +        VpnStatus.logError("[obfsvpn] " + s); +    } + +    @Override +    public void log(String state, String message) { +        VpnStatus.logDebug("[obfsvpn] " + state + " " + message); +    } + +} 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/de/blinkt/openvpn/VpnProfileTest.java b/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java index e8a93b75..9675c877 100644 --- a/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java +++ b/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java @@ -1,32 +1,43 @@  package de.blinkt.openvpn; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_KCP; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN; +  import org.json.JSONException;  import org.json.JSONObject;  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.UUID;  import de.blinkt.openvpn.core.connection.Obfs4Connection;  import de.blinkt.openvpn.core.connection.OpenvpnConnection; +import se.leap.bitmaskclient.base.utils.ConfigHelper;  import se.leap.bitmaskclient.pluggableTransports.Obfs4Options; -import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; -import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.powermock.api.mockito.PowerMockito.mockStatic; - -@PrepareForTest({UUID.class}) +@RunWith(PowerMockRunner.class) +@PrepareForTest({UUID.class, ConfigHelper.ObfsVpnHelper.class})  public class VpnProfileTest { -    private static final String OPENVPNCONNECTION_PROFILE   = "{\"mAuthenticationType\":2,\"mName\":\"mockProfile\",\"mTLSAuthDirection\":\"\",\"mUseLzo\":false,\"mUseTLSAuth\":false,\"mDNS1\":\"8.8.8.8\",\"mDNS2\":\"8.8.4.4\",\"mOverrideDNS\":false,\"mSearchDomain\":\"blinkt.de\",\"mUseDefaultRoute\":true,\"mUsePull\":true,\"mCheckRemoteCN\":true,\"mExpectTLSCert\":false,\"mRemoteCN\":\"\",\"mPassword\":\"\",\"mUsername\":\"\",\"mRoutenopull\":false,\"mUseRandomHostname\":false,\"mUseFloat\":false,\"mUseCustomConfig\":false,\"mCustomConfigOptions\":\"\",\"mVerb\":\"1\",\"mCipher\":\"\",\"mDataCiphers\":\"\",\"mNobind\":true,\"mUseDefaultRoutev6\":true,\"mCustomRoutesv6\":\"\",\"mKeyPassword\":\"\",\"mPersistTun\":false,\"mConnectRetryMax\":\"-1\",\"mConnectRetry\":\"2\",\"mConnectRetryMaxTime\":\"300\",\"mUserEditable\":true,\"mAuth\":\"\",\"mX509AuthType\":3,\"mAllowLocalLAN\":false,\"mMssFix\":0,\"mConnections\":[{\"mServerName\":\"openvpn.example.com\",\"mServerPort\":\"1194\",\"mUseUdp\":false,\"mCustomConfiguration\":\"\",\"mUseCustomConfig\":false,\"mEnabled\":true,\"mConnectTimeout\":0,\"mProxyType\":\"NONE\",\"mProxyName\":\"proxy.example.com\",\"mProxyPort\":\"8080\",\"mUseProxyAuth\":false,\"ConnectionAdapter.META_TYPE\":\"de.blinkt.openvpn.core.connection.OpenvpnConnection\"}],\"mRemoteRandom\":false,\"mAllowedAppsVpn\":[],\"mAllowedAppsVpnAreDisallowed\":true,\"mAllowAppVpnBypass\":false,\"mAuthRetry\":0,\"mTunMtu\":0,\"mPushPeerInfo\":false,\"mVersion\":0,\"mLastUsed\":0,\"mServerName\":\"openvpn.example.com\",\"mServerPort\":\"1194\",\"mUseUdp\":true,\"mTemporaryProfile\":false,\"mUuid\":\"9d295ca2-3789-48dd-996e-f731dbf50fdc\",\"mProfileVersion\":7,\"mBlockUnusedAddressFamilies\":true,\"mUsePluggableTransports\":false}"; -    private static final String OBFS4CONNECTION_PROFILE = "{\"mAuthenticationType\":2,\"mName\":\"mockProfile\",\"mTLSAuthDirection\":\"\",\"mUseLzo\":false,\"mUseTLSAuth\":false,\"mDNS1\":\"8.8.8.8\",\"mDNS2\":\"8.8.4.4\",\"mOverrideDNS\":false,\"mSearchDomain\":\"blinkt.de\",\"mUseDefaultRoute\":true,\"mUsePull\":true,\"mCheckRemoteCN\":true,\"mExpectTLSCert\":false,\"mRemoteCN\":\"\",\"mPassword\":\"\",\"mUsername\":\"\",\"mRoutenopull\":false,\"mUseRandomHostname\":false,\"mUseFloat\":false,\"mUseCustomConfig\":false,\"mCustomConfigOptions\":\"\",\"mVerb\":\"1\",\"mCipher\":\"\",\"mDataCiphers\":\"\",\"mNobind\":true,\"mUseDefaultRoutev6\":true,\"mCustomRoutesv6\":\"\",\"mKeyPassword\":\"\",\"mPersistTun\":false,\"mConnectRetryMax\":\"-1\",\"mConnectRetry\":\"2\",\"mConnectRetryMaxTime\":\"300\",\"mUserEditable\":true,\"mAuth\":\"\",\"mX509AuthType\":3,\"mAllowLocalLAN\":false,\"mMssFix\":0,\"mConnections\":[{\"options\":{\"cert\":\"CERT\",\"iatMode\":\"1\",\"remoteIP\":\"192.168.0.1\",\"remotePort\":\"1234\"},\"mServerName\":\"127.0.0.1\",\"mServerPort\":\"4430\",\"mUseUdp\":false,\"mCustomConfiguration\":\"\",\"mUseCustomConfig\":false,\"mEnabled\":true,\"mConnectTimeout\":0,\"mProxyType\":\"NONE\",\"mProxyName\":\"\",\"mProxyPort\":\"\",\"mUseProxyAuth\":false,\"ConnectionAdapter.META_TYPE\":\"de.blinkt.openvpn.core.connection.Obfs4Connection\"}],\"mRemoteRandom\":false,\"mAllowedAppsVpn\":[],\"mAllowedAppsVpnAreDisallowed\":true,\"mAllowAppVpnBypass\":false,\"mAuthRetry\":0,\"mTunMtu\":0,\"mPushPeerInfo\":false,\"mVersion\":0,\"mLastUsed\":0,\"mServerName\":\"openvpn.example.com\",\"mServerPort\":\"1194\",\"mUseUdp\":true,\"mTemporaryProfile\":false,\"mUuid\":\"9d295ca2-3789-48dd-996e-f731dbf50fdc\",\"mProfileVersion\":7,\"mBlockUnusedAddressFamilies\":true,\"mUsePluggableTransports\":true}"; +    private static final String OPENVPNCONNECTION_PROFILE   = "{\"mCipher\":\"\",\"mProfileVersion\":7,\"mLastUsed\":0,\"mCheckRemoteCN\":true,\"mVerb\":\"1\",\"mRemoteRandom\":false,\"mRoutenopull\":false,\"mConnectRetry\":\"2\",\"mAllowedAppsVpn\":[],\"mUserEditable\":true,\"mUseUdp\":true,\"mAllowedAppsVpnAreDisallowed\":true,\"mDNS1\":\"8.8.8.8\",\"mDNS2\":\"8.8.4.4\",\"mUseCustomConfig\":false,\"mUseFloat\":false,\"mUseDefaultRoute\":true,\"mConnectRetryMaxTime\":\"300\",\"mNobind\":true,\"mVersion\":0,\"mConnectRetryMax\":\"-1\",\"mOverrideDNS\":false,\"mAuth\":\"\",\"mTunMtu\":0,\"mUseObfs4\":false,\"mPassword\":\"\",\"mTLSAuthDirection\":\"\",\"mKeyPassword\":\"\",\"mUseObfs4Kcp\":false,\"mCustomConfigOptions\":\"\",\"mName\":\"mockProfile\",\"mExpectTLSCert\":false,\"mUsername\":\"\",\"mAllowLocalLAN\":false,\"mDataCiphers\":\"\",\"mSearchDomain\":\"blinkt.de\",\"mTemporaryProfile\":false,\"mUseTLSAuth\":false,\"mRemoteCN\":\"\",\"mCustomRoutesv6\":\"\",\"mPersistTun\":false,\"mX509AuthType\":3,\"mUuid\":\"9d295ca2-3789-48dd-996e-f731dbf50fdc\",\"mServerName\":\"openvpn.example.com\",\"mMssFix\":0,\"mPushPeerInfo\":false,\"mAuthenticationType\":2,\"mBlockUnusedAddressFamilies\":true,\"mServerPort\":\"1194\",\"mUseDefaultRoutev6\":true,\"mConnections\":[{\"mCustomConfiguration\":\"\",\"mUseUdp\":false,\"mServerName\":\"openvpn.example.com\",\"mProxyType\":\"NONE\",\"mProxyPort\":\"8080\",\"mUseCustomConfig\":false,\"mConnectTimeout\":0,\"mProxyName\":\"proxy.example.com\",\"mUseProxyAuth\":false,\"ConnectionAdapter.META_TYPE\":\"de.blinkt.openvpn.core.connection.OpenvpnConnection\",\"mServerPort\":\"1194\",\"mEnabled\":true}],\"mUseLzo\":false,\"mAllowAppVpnBypass\":false,\"mUsePull\":true,\"mUseRandomHostname\":false,\"mAuthRetry\":0}"; +    private static final String OBFS4CONNECTION_PROFILE = "{\"mCipher\":\"\",\"mProfileVersion\":7,\"mLastUsed\":0,\"mCheckRemoteCN\":true,\"mVerb\":\"1\",\"mRemoteRandom\":false,\"mRoutenopull\":false,\"mConnectRetry\":\"2\",\"mAllowedAppsVpn\":[],\"mUserEditable\":true,\"mUseUdp\":true,\"mAllowedAppsVpnAreDisallowed\":true,\"mDNS1\":\"8.8.8.8\",\"mDNS2\":\"8.8.4.4\",\"mUseCustomConfig\":false,\"mUseFloat\":false,\"mUseDefaultRoute\":true,\"mConnectRetryMaxTime\":\"300\",\"mNobind\":true,\"mVersion\":0,\"mConnectRetryMax\":\"-1\",\"mOverrideDNS\":false,\"mAuth\":\"\",\"mTunMtu\":0,\"mUseObfs4\":true,\"mPassword\":\"\",\"mTLSAuthDirection\":\"\",\"mKeyPassword\":\"\",\"mUseObfs4Kcp\":false,\"mCustomConfigOptions\":\"\",\"mName\":\"mockProfile\",\"mExpectTLSCert\":false,\"mUsername\":\"\",\"mAllowLocalLAN\":false,\"mDataCiphers\":\"\",\"mSearchDomain\":\"blinkt.de\",\"mTemporaryProfile\":false,\"mUseTLSAuth\":false,\"mRemoteCN\":\"\",\"mCustomRoutesv6\":\"\",\"mPersistTun\":false,\"mX509AuthType\":3,\"mUuid\":\"9d295ca2-3789-48dd-996e-f731dbf50fdc\",\"mServerName\":\"openvpn.example.com\",\"mMssFix\":0,\"mPushPeerInfo\":false,\"mAuthenticationType\":2,\"mBlockUnusedAddressFamilies\":true,\"mServerPort\":\"1194\",\"mUseDefaultRoutev6\":true,\"mConnections\":[{\"mCustomConfiguration\":\"\",\"mServerName\":\"127.0.0.1\",\"mProxyType\":\"NONE\",\"mConnectTimeout\":0,\"mServerPort\":\"4430\",\"mUseUdp\":false,\"mProxyPort\":\"\",\"mUseCustomConfig\":false,\"options\":{\"udp\":false,\"remoteIP\":\"192.168.0.1\",\"iatMode\":\"1\",\"remotePort\":\"1234\",\"cert\":\"CERT\"},\"mProxyName\":\"\",\"mUseProxyAuth\":false,\"ConnectionAdapter.META_TYPE\":\"de.blinkt.openvpn.core.connection.Obfs4Connection\",\"mEnabled\":true}],\"mUseLzo\":false,\"mAllowAppVpnBypass\":false,\"mUsePull\":true,\"mUseRandomHostname\":false,\"mAuthRetry\":0}"; +    private static final String OBFS4CONNECTION_PROFILE_OBFSVPN = "{\"mCipher\":\"\",\"mProfileVersion\":7,\"mLastUsed\":0,\"mCheckRemoteCN\":true,\"mVerb\":\"1\",\"mRemoteRandom\":false,\"mRoutenopull\":false,\"mConnectRetry\":\"2\",\"mAllowedAppsVpn\":[],\"mUserEditable\":true,\"mUseUdp\":true,\"mAllowedAppsVpnAreDisallowed\":true,\"mDNS1\":\"8.8.8.8\",\"mDNS2\":\"8.8.4.4\",\"mUseCustomConfig\":false,\"mUseFloat\":false,\"mUseDefaultRoute\":true,\"mConnectRetryMaxTime\":\"300\",\"mNobind\":true,\"mVersion\":0,\"mConnectRetryMax\":\"-1\",\"mOverrideDNS\":false,\"mAuth\":\"\",\"mTunMtu\":0,\"mUseObfs4\":true,\"mPassword\":\"\",\"mTLSAuthDirection\":\"\",\"mKeyPassword\":\"\",\"mUseObfs4Kcp\":false,\"mCustomConfigOptions\":\"\",\"mName\":\"mockProfile\",\"mExpectTLSCert\":false,\"mUsername\":\"\",\"mAllowLocalLAN\":false,\"mDataCiphers\":\"\",\"mSearchDomain\":\"blinkt.de\",\"mTemporaryProfile\":false,\"mUseTLSAuth\":false,\"mRemoteCN\":\"\",\"mCustomRoutesv6\":\"\",\"mPersistTun\":false,\"mX509AuthType\":3,\"mUuid\":\"9d295ca2-3789-48dd-996e-f731dbf50fdc\",\"mServerName\":\"openvpn.example.com\",\"mMssFix\":0,\"mPushPeerInfo\":false,\"mAuthenticationType\":2,\"mBlockUnusedAddressFamilies\":true,\"mServerPort\":\"1194\",\"mUseDefaultRoutev6\":true,\"mConnections\":[{\"mCustomConfiguration\":\"\",\"mServerName\":\"192.168.0.1\",\"mProxyType\":\"SOCKS5\",\"mConnectTimeout\":0,\"mServerPort\":\"1234\",\"mUseUdp\":false,\"mProxyPort\":\"4430\",\"mUseCustomConfig\":false,\"options\":{\"udp\":false,\"remoteIP\":\"192.168.0.1\",\"iatMode\":\"1\",\"remotePort\":\"1234\",\"cert\":\"CERT\"},\"mProxyName\":\"127.0.0.1\",\"mUseProxyAuth\":false,\"ConnectionAdapter.META_TYPE\":\"de.blinkt.openvpn.core.connection.Obfs4Connection\",\"mEnabled\":true}],\"mUseLzo\":false,\"mAllowAppVpnBypass\":false,\"mUsePull\":true,\"mUseRandomHostname\":false,\"mAuthRetry\":0}"; +    private static final String OBFS4CONNECTION_PROFILE_OBFSVPN_KCP = "{\"mCipher\":\"\",\"mProfileVersion\":7,\"mLastUsed\":0,\"mCheckRemoteCN\":true,\"mVerb\":\"1\",\"mRemoteRandom\":false,\"mRoutenopull\":false,\"mConnectRetry\":\"2\",\"mAllowedAppsVpn\":[],\"mUserEditable\":true,\"mUseUdp\":true,\"mAllowedAppsVpnAreDisallowed\":true,\"mDNS1\":\"8.8.8.8\",\"mDNS2\":\"8.8.4.4\",\"mUseCustomConfig\":false,\"mUseFloat\":false,\"mUseDefaultRoute\":true,\"mConnectRetryMaxTime\":\"300\",\"mNobind\":true,\"mVersion\":0,\"mConnectRetryMax\":\"-1\",\"mOverrideDNS\":false,\"mAuth\":\"\",\"mTunMtu\":0,\"mUseObfs4\":false,\"mPassword\":\"\",\"mTLSAuthDirection\":\"\",\"mKeyPassword\":\"\",\"mUseObfs4Kcp\":true,\"mCustomConfigOptions\":\"\",\"mName\":\"mockProfile\",\"mExpectTLSCert\":false,\"mUsername\":\"\",\"mAllowLocalLAN\":false,\"mDataCiphers\":\"\",\"mSearchDomain\":\"blinkt.de\",\"mTemporaryProfile\":false,\"mUseTLSAuth\":false,\"mRemoteCN\":\"\",\"mCustomRoutesv6\":\"\",\"mPersistTun\":false,\"mX509AuthType\":3,\"mUuid\":\"9d295ca2-3789-48dd-996e-f731dbf50fdc\",\"mServerName\":\"openvpn.example.com\",\"mMssFix\":0,\"mPushPeerInfo\":false,\"mAuthenticationType\":2,\"mBlockUnusedAddressFamilies\":true,\"mServerPort\":\"1194\",\"mUseDefaultRoutev6\":true,\"mConnections\":[{\"mCustomConfiguration\":\"\",\"mServerName\":\"192.168.0.1\",\"mProxyType\":\"SOCKS5\",\"mConnectTimeout\":0,\"mServerPort\":\"1234\",\"mUseUdp\":false,\"mProxyPort\":\"4430\",\"mUseCustomConfig\":false,\"options\":{\"udp\":true,\"remoteIP\":\"192.168.0.1\",\"iatMode\":\"1\",\"remotePort\":\"1234\",\"cert\":\"CERT\"},\"mProxyName\":\"127.0.0.1\",\"mUseProxyAuth\":false,\"ConnectionAdapter.META_TYPE\":\"de.blinkt.openvpn.core.connection.Obfs4Connection\",\"mEnabled\":true}],\"mUseLzo\":false,\"mAllowAppVpnBypass\":false,\"mUsePull\":true,\"mUseRandomHostname\":false,\"mAuthRetry\":0}\n"; +      @Before      public void setup() {          mockStatic(UUID.class); +        mockStatic(ConfigHelper.ObfsVpnHelper.class);      }      @Test @@ -58,9 +69,10 @@ public class VpnProfileTest {      @Test      public void toJson_obfs4() throws JSONException { +        when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(false); +          VpnProfile mockVpnProfile = new VpnProfile("mockProfile", OBFS4); -        mockVpnProfile.mConnections[0] = new Obfs4Connection(new Obfs4Options("192.168.0.1", "1234", "CERT", "1")); -        mockVpnProfile.mConnections[0].setUseUdp(false); +        mockVpnProfile.mConnections[0] = new Obfs4Connection(new Obfs4Options("192.168.0.1", "1234", "CERT", "1", false));          mockVpnProfile.mLastUsed = 0;          String s = mockVpnProfile.toJson();          System.out.println(s); @@ -74,17 +86,91 @@ public class VpnProfileTest {      }      @Test +    public void toJson_obfs4_obfsvpn() throws JSONException { +        when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); +        VpnProfile mockVpnProfile = new VpnProfile("mockProfile", OBFS4); +        mockVpnProfile.mConnections[0] = new Obfs4Connection(new Obfs4Options("192.168.0.1", "1234", "CERT", "1", false)); +        mockVpnProfile.mLastUsed = 0; +        String s = mockVpnProfile.toJson(); +        System.out.println(s); + +        //ignore UUID in comparison -> set it to fixed value +        JSONObject actual = new JSONObject(s); +        actual.put("mUuid", "9d295ca2-3789-48dd-996e-f731dbf50fdc"); +        JSONObject expectation = new JSONObject(OBFS4CONNECTION_PROFILE_OBFSVPN); + +        assertEquals(expectation.toString(),actual.toString()); +    } + +    @Test +    public void toJson_obfs4_obfsvpn_kcp() throws JSONException { +        when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); + +        VpnProfile mockVpnProfile = new VpnProfile("mockProfile", OBFS4_KCP); +        mockVpnProfile.mConnections[0] = new Obfs4Connection(new Obfs4Options("192.168.0.1", "1234", "CERT", "1", true)); +        mockVpnProfile.mLastUsed = 0; +        String s = mockVpnProfile.toJson(); +        System.out.println(s); + +        //ignore UUID in comparison -> set it to fixed value +        JSONObject actual = new JSONObject(s); +        actual.put("mUuid", "9d295ca2-3789-48dd-996e-f731dbf50fdc"); +        JSONObject expectation = new JSONObject(OBFS4CONNECTION_PROFILE_OBFSVPN_KCP); + +        assertEquals(expectation.toString(),actual.toString()); +    } + +    @Test      public void fromJson_obfs4() { +        when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(false); +          VpnProfile mockVpnProfile = VpnProfile.fromJson(OBFS4CONNECTION_PROFILE);          assertNotNull(mockVpnProfile);          assertNotNull(mockVpnProfile.mConnections);          assertNotNull(mockVpnProfile.mConnections[0]);          assertFalse(mockVpnProfile.mConnections[0].isUseUdp());          Obfs4Connection obfs4Connection = (Obfs4Connection) mockVpnProfile.mConnections[0]; -        assertEquals(obfs4Connection.getTransportType(), OBFS4); -        assertEquals(obfs4Connection.getDispatcherOptions().cert, "CERT"); -        assertEquals(obfs4Connection.getDispatcherOptions().iatMode, "1"); -        assertEquals(obfs4Connection.getDispatcherOptions().remoteIP, "192.168.0.1"); -        assertEquals(obfs4Connection.getDispatcherOptions().remotePort, "1234"); +        assertEquals(OBFS4, obfs4Connection.getTransportType()); +        assertFalse(obfs4Connection.getDispatcherOptions().udp); +        assertEquals("CERT", obfs4Connection.getDispatcherOptions().cert); +        assertEquals("1", obfs4Connection.getDispatcherOptions().iatMode); +        assertEquals("192.168.0.1", obfs4Connection.getDispatcherOptions().remoteIP); +        assertEquals("1234", obfs4Connection.getDispatcherOptions().remotePort); +    } + +    @Test +    public void fromJson_obfs4_obfsvpn() { +        when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); + +        VpnProfile mockVpnProfile = VpnProfile.fromJson(OBFS4CONNECTION_PROFILE_OBFSVPN); +        assertNotNull(mockVpnProfile); +        assertNotNull(mockVpnProfile.mConnections); +        assertNotNull(mockVpnProfile.mConnections[0]); +        assertFalse(mockVpnProfile.mConnections[0].isUseUdp()); +        Obfs4Connection obfs4Connection = (Obfs4Connection) mockVpnProfile.mConnections[0]; +        assertEquals(OBFS4, obfs4Connection.getTransportType()); +        assertFalse(obfs4Connection.getDispatcherOptions().udp); +        assertEquals("CERT", obfs4Connection.getDispatcherOptions().cert); +        assertEquals("1", obfs4Connection.getDispatcherOptions().iatMode); +        assertEquals("192.168.0.1", obfs4Connection.getDispatcherOptions().remoteIP); +        assertEquals("1234", obfs4Connection.getDispatcherOptions().remotePort); +    } + +    @Test +    public void fromJson_obfs4_obfsvpn_kcp() { +        when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); + +        VpnProfile mockVpnProfile = VpnProfile.fromJson(OBFS4CONNECTION_PROFILE_OBFSVPN_KCP); +        assertNotNull(mockVpnProfile); +        assertNotNull(mockVpnProfile.mConnections); +        assertNotNull(mockVpnProfile.mConnections[0]); +        assertFalse(mockVpnProfile.mConnections[0].isUseUdp()); +        Obfs4Connection obfs4Connection = (Obfs4Connection) mockVpnProfile.mConnections[0]; +        assertEquals(OBFS4, obfs4Connection.getTransportType()); +        assertTrue(obfs4Connection.getDispatcherOptions().udp); +        assertEquals("CERT", obfs4Connection.getDispatcherOptions().cert); +        assertEquals("1", obfs4Connection.getDispatcherOptions().iatMode); +        assertEquals("192.168.0.1", obfs4Connection.getDispatcherOptions().remoteIP); +        assertEquals("1234", obfs4Connection.getDispatcherOptions().remotePort);      }  }
\ 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/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java index 729abcdf..56242396 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java @@ -22,6 +22,7 @@ import de.blinkt.openvpn.VpnProfile;  import de.blinkt.openvpn.core.ConfigParser;  import de.blinkt.openvpn.core.connection.Connection;  import se.leap.bitmaskclient.base.models.Location; +import se.leap.bitmaskclient.base.models.Pair;  import se.leap.bitmaskclient.base.models.Provider;  import se.leap.bitmaskclient.base.models.ProviderObservable;  import se.leap.bitmaskclient.base.utils.ConfigHelper; @@ -31,6 +32,7 @@ import se.leap.bitmaskclient.testutils.MockSharedPreferences;  import se.leap.bitmaskclient.testutils.TestSetupHelper;  import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_KCP;  import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN;  import static junit.framework.Assert.assertEquals;  import static junit.framework.Assert.assertNull; @@ -115,7 +117,7 @@ public class GatewaysManagerTest {          MockHelper.mockProviderObservable(provider);          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3, false); +        VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3, false, false);          VpnProfile profile = configGenerator.createProfile(OBFS4);          profile.mGatewayIp = "37.218.247.60"; @@ -130,7 +132,7 @@ public class GatewaysManagerTest {          MockHelper.mockProviderObservable(provider);          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3, false); +        VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3, false, false);          VpnProfile profile = configGenerator.createProfile(OPENVPN);          profile.mGatewayIp = "37.218.247.60"; @@ -145,7 +147,7 @@ public class GatewaysManagerTest {          MockHelper.mockProviderObservable(provider);          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3, false); +        VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3, false, false);          VpnProfile profile = configGenerator.createProfile(OBFS4);          profile.mGatewayIp = "37.218.247.60"; @@ -160,7 +162,7 @@ public class GatewaysManagerTest {          MockHelper.mockProviderObservable(provider);          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3, false); +        VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3, false, false);          VpnProfile profile = configGenerator.createProfile(OPENVPN);          profile.mGatewayIp = "37.218.247.60"; @@ -175,7 +177,7 @@ public class GatewaysManagerTest {          MockHelper.mockProviderObservable(provider);          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3, false); +        VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3, false, false);          VpnProfile profile = configGenerator.createProfile(OBFS4);          profile.mGatewayIp = "37.218.247.61"; @@ -190,7 +192,7 @@ public class GatewaysManagerTest {          MockHelper.mockProviderObservable(provider);          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3, false); +        VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3, false, false);          VpnProfile profile = configGenerator.createProfile(OBFS4);          profile.mGatewayIp = "3.21.247.89"; @@ -206,7 +208,7 @@ public class GatewaysManagerTest {          when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true);          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        assertEquals("37.12.247.10", gatewaysManager.select(0).getRemoteIP()); +        assertEquals("37.12.247.10", gatewaysManager.select(0).first.getRemoteIP());      }      @Test @@ -219,9 +221,9 @@ public class GatewaysManagerTest {          when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        assertEquals("manila.bitmask.net", gatewaysManager.select(0).getHost()); -        assertEquals("moscow.bitmask.net", gatewaysManager.select(1).getHost()); -        assertEquals("pt.demo.bitmask.net", gatewaysManager.select(2).getHost()); +        assertEquals("manila.bitmask.net", gatewaysManager.select(0).first.getHost()); +        assertEquals("moscow.bitmask.net", gatewaysManager.select(1).first.getHost()); +        assertEquals("pt.demo.bitmask.net", gatewaysManager.select(2).first.getHost());      }      @Test @@ -234,8 +236,8 @@ public class GatewaysManagerTest {          when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true);          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        assertEquals("moscow.bitmask.net", gatewaysManager.select(0).getHost()); -        assertEquals("pt.demo.bitmask.net", gatewaysManager.select(1).getHost()); +        assertEquals("moscow.bitmask.net", gatewaysManager.select(0).first.getHost()); +        assertEquals("pt.demo.bitmask.net", gatewaysManager.select(1).first.getHost());          assertNull(gatewaysManager.select(2));      } @@ -251,9 +253,9 @@ public class GatewaysManagerTest {          when(PreferenceHelper.getPreferredCity(any(Context.class))).thenReturn("Paris");          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        assertEquals("mouette.riseup.net", gatewaysManager.select(0).getHost()); -        assertEquals("hoatzin.riseup.net", gatewaysManager.select(1).getHost()); -        assertEquals("zarapito.riseup.net", gatewaysManager.select(2).getHost()); +        assertEquals("mouette.riseup.net", gatewaysManager.select(0).first.getHost()); +        assertEquals("hoatzin.riseup.net", gatewaysManager.select(1).first.getHost()); +        assertEquals("zarapito.riseup.net", gatewaysManager.select(2).first.getHost());      }      @Test @@ -267,9 +269,9 @@ public class GatewaysManagerTest {          when(PreferenceHelper.getPreferredCity(any(Context.class))).thenReturn("Paris");          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        assertEquals("mouette.riseup.net", gatewaysManager.select(0).getHost()); -        assertEquals("hoatzin.riseup.net", gatewaysManager.select(1).getHost()); -        assertEquals("zarapito.riseup.net", gatewaysManager.select(2).getHost()); +        assertEquals("mouette.riseup.net", gatewaysManager.select(0).first.getHost()); +        assertEquals("hoatzin.riseup.net", gatewaysManager.select(1).first.getHost()); +        assertEquals("zarapito.riseup.net", gatewaysManager.select(2).first.getHost());      }      @Test @@ -284,9 +286,9 @@ public class GatewaysManagerTest {          when(PreferenceHelper.getPreferredCity(any(Context.class))).thenReturn("Paris");          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        assertEquals("Paris", gatewaysManager.select(0).getName()); -        assertEquals("Paris", gatewaysManager.select(1).getName()); -        assertEquals("Paris", gatewaysManager.select(2).getName()); +        assertEquals("Paris", gatewaysManager.select(0).first.getName()); +        assertEquals("Paris", gatewaysManager.select(1).first.getName()); +        assertEquals("Paris", gatewaysManager.select(2).first.getName());          assertEquals(null, gatewaysManager.select(3));      } @@ -300,9 +302,9 @@ public class GatewaysManagerTest {          when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        assertEquals("mouette.riseup.net", gatewaysManager.select(0, "Paris").getHost()); -        assertEquals("hoatzin.riseup.net", gatewaysManager.select(1, "Paris").getHost()); -        assertEquals("zarapito.riseup.net", gatewaysManager.select(2, "Paris").getHost()); +        assertEquals("mouette.riseup.net", gatewaysManager.select(0, "Paris").first.getHost()); +        assertEquals("hoatzin.riseup.net", gatewaysManager.select(1, "Paris").first.getHost()); +        assertEquals("zarapito.riseup.net", gatewaysManager.select(2, "Paris").first.getHost());      }      @Test @@ -315,9 +317,9 @@ public class GatewaysManagerTest {          when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        assertEquals("mouette.riseup.net", gatewaysManager.select(0, "Paris").getHost()); -        assertEquals("hoatzin.riseup.net", gatewaysManager.select(1, "Paris").getHost()); -        assertEquals("zarapito.riseup.net", gatewaysManager.select(2, "Paris").getHost()); +        assertEquals("mouette.riseup.net", gatewaysManager.select(0, "Paris").first.getHost()); +        assertEquals("hoatzin.riseup.net", gatewaysManager.select(1, "Paris").first.getHost()); +        assertEquals("zarapito.riseup.net", gatewaysManager.select(2, "Paris").first.getHost());      }      @Test @@ -331,9 +333,9 @@ public class GatewaysManagerTest {          when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);          GatewaysManager gatewaysManager = new GatewaysManager(mockContext); -        assertEquals("Paris", gatewaysManager.select(0, "Paris").getName()); -        assertEquals("Paris", gatewaysManager.select(1, "Paris").getName()); -        assertEquals("Paris", gatewaysManager.select(2, "Paris").getName()); +        assertEquals("Paris", gatewaysManager.select(0, "Paris").first.getName()); +        assertEquals("Paris", gatewaysManager.select(1, "Paris").first.getName()); +        assertEquals("Paris", gatewaysManager.select(2, "Paris").first.getName());          assertEquals(null, gatewaysManager.select(3, "Paris"));      } @@ -509,6 +511,38 @@ public class GatewaysManagerTest {          assertEquals("Amsterdam", locations.get(2).getName());      } +    // Currently all pluggable transports are handled the same, there's no UI to select a specific one +    // when requesting a sorted list for any pluggabled transport, a sorted list of all pluiggable transports +    // will be returned +    @Test +    public void testGetSortedLocations_obfs4kcp_generalizedAsPT() { +        Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", "v4/riseup_geoip_v4_bad_obfs4_gateway.json"); + +        MockHelper.mockProviderObservable(provider); +        mockStatic(PreferenceHelper.class); +        when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false); +        GatewaysManager gatewaysManager = new GatewaysManager(mockContext); +        List<Location> locations = gatewaysManager.getSortedGatewayLocations(OBFS4_KCP); + +        assertEquals(3, locations.size()); +    } + +    @Test +    public void testgetAverageLoad_isSameForAllTransports() { +        Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_kcp_gateways.json", "ptdemo_kcp_gateways_geoip.json"); + +        MockHelper.mockProviderObservable(provider); +        mockStatic(PreferenceHelper.class); +        when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false); +        GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + +        assertEquals(0.3, gatewaysManager.getLocation("Amsterdam").getAverageLoad(OBFS4_KCP)); +        assertEquals(0.3, gatewaysManager.getLocation("Amsterdam").getAverageLoad(OBFS4)); +        assertEquals(0.3, gatewaysManager.getLocation("Amsterdam").getAverageLoad(OPENVPN)); +    } + + +      @Test      public void testGetLoadForLocation_() {          MockHelper.mockProviderObservable(null); diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java index 2b1615da..45a20b1c 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java @@ -65,7 +65,6 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOAD  import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE;  import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;  import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_GEOIP_JSON; -import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE;  import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE;  import static se.leap.bitmaskclient.providersetup.ProviderAPI.MISSING_NETWORK_CONNECTION;  import static se.leap.bitmaskclient.providersetup.ProviderAPI.PARAMETERS; @@ -85,7 +84,6 @@ import static se.leap.bitmaskclient.testutils.MockHelper.mockBase64;  import static se.leap.bitmaskclient.testutils.MockHelper.mockBundle;  import static se.leap.bitmaskclient.testutils.MockHelper.mockClientGenerator;  import static se.leap.bitmaskclient.testutils.MockHelper.mockConfigHelper; -import static se.leap.bitmaskclient.testutils.MockHelper.mockConfigHelper;  import static se.leap.bitmaskclient.testutils.MockHelper.mockIntent;  import static se.leap.bitmaskclient.testutils.MockHelper.mockPreferenceHelper;  import static se.leap.bitmaskclient.testutils.MockHelper.mockProviderApiConnector; 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 0fd07858..f0db65d9 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java @@ -1,5 +1,19 @@  package se.leap.bitmaskclient.eip; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_KCP; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN; +import static se.leap.bitmaskclient.base.models.Constants.OPENVPN_CONFIGURATION; +import static se.leap.bitmaskclient.testutils.MockHelper.mockTextUtils; +  import android.content.Context;  import android.content.SharedPreferences;  import android.preference.PreferenceManager; @@ -20,27 +34,15 @@ import java.util.HashMap;  import de.blinkt.openvpn.VpnProfile;  import de.blinkt.openvpn.core.connection.Connection; +import se.leap.bitmaskclient.base.utils.ConfigHelper;  import se.leap.bitmaskclient.testutils.MockHelper;  import se.leap.bitmaskclient.testutils.TestSetupHelper; -import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; -import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.RETURNS_DEEP_STUBS; -import static org.mockito.Mockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; -import static se.leap.bitmaskclient.base.models.Constants.OPENVPN_CONFIGURATION; -import static se.leap.bitmaskclient.testutils.MockHelper.mockTextUtils; -  /**   * Created by cyberta on 03.10.17.   */  @RunWith(PowerMockRunner.class) -@PrepareForTest({Log.class, TextUtils.class, PreferenceManager.class}) +@PrepareForTest({Log.class, TextUtils.class, PreferenceManager.class, ConfigHelper.ObfsVpnHelper.class})  public class VpnConfigGeneratorTest {      @Mock(answer = Answers.RETURNS_DEEP_STUBS) @@ -164,6 +166,8 @@ public class VpnConfigGeneratorTest {              "persist-tun\n" +              "# persist-tun also enables pre resolving to avoid DNS resolve problem\n" +              "preresolve\n" + +            "# Use system proxy setting\n" + +            "management-query-proxy\n" +              "# Custom configuration options\n" +              "# You are on your on own here :)\n" +              "# These options found in the config file do not map to config settings:\n" + @@ -283,6 +287,8 @@ public class VpnConfigGeneratorTest {              "persist-tun\n" +              "# persist-tun also enables pre resolving to avoid DNS resolve problem\n" +              "preresolve\n" + +            "# Use system proxy setting\n" + +            "management-query-proxy\n" +              "# Custom configuration options\n" +              "# You are on your on own here :)\n" +              "# These options found in the config file do not map to config settings:\n" + @@ -402,6 +408,129 @@ public class VpnConfigGeneratorTest {              "persist-tun\n" +              "# persist-tun also enables pre resolving to avoid DNS resolve problem\n" +              "preresolve\n" + +            "# Use system proxy setting\n" + +            "management-query-proxy\n" + +            "# Custom configuration options\n" + +            "# You are on your on own here :)\n" + +            "# These options found in the config file do not map to config settings:\n" + +            "keepalive 10 30 \n" + +            "tls-cipher DHE-RSA-AES128-SHA \n"; + +    String expectedVPNConfig_v3_obfsvpn_obfs4 = "# Config for OpenVPN 2.x\n" + +            "# Enables connection to GUI\n" + +            "management /data/data/se.leap.bitmask/mgmtsocket unix\n" + +            "management-client\n" + +            "management-query-passwords\n" + +            "management-hold\n" + +            "\n" + +            "setenv IV_GUI_VER \"se.leap.bitmaskclient 0.9.10\" \n" + +            "setenv IV_PLAT_VER \"0 null JUNIT null null null\"\n" + +            "machine-readable-output\n" + +            "allow-recursive-routing\n" + +            "ifconfig-nowarn\n" + +            "client\n" + +            "verb 4\n" + +            "connect-retry 2 300\n" + +            "resolv-retry 60\n" + +            "dev tun\n" + +            "remote 37.218.247.60 23049 tcp-client\n" + +            "<ca>\n" + +            "-----BEGIN CERTIFICATE-----\n" + +            "MIIFbzCCA1egAwIBAgIBATANBgkqhkiG9w0BAQ0FADBKMRgwFgYDVQQDDA9CaXRt\n" + +            "YXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNVBAsME2h0dHBzOi8v\n" + +            "Yml0bWFzay5uZXQwHhcNMTIxMTA2MDAwMDAwWhcNMjIxMTA2MDAwMDAwWjBKMRgw\n" + +            "FgYDVQQDDA9CaXRtYXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNV\n" + +            "BAsME2h0dHBzOi8vYml0bWFzay5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n" + +            "ggIKAoICAQC1eV4YvayaU+maJbWrD4OHo3d7S1BtDlcvkIRS1Fw3iYDjsyDkZxai\n" + +            "dHp4EUasfNQ+EVtXUvtk6170EmLco6Elg8SJBQ27trE6nielPRPCfX3fQzETRfvB\n" + +            "7tNvGw4Jn2YKiYoMD79kkjgyZjkJ2r/bEHUSevmR09BRp86syHZerdNGpXYhcQ84\n" + +            "CA1+V+603GFIHnrP+uQDdssW93rgDNYu+exT+Wj6STfnUkugyjmPRPjL7wh0tzy+\n" + +            "znCeLl4xiV3g9sjPnc7r2EQKd5uaTe3j71sDPF92KRk0SSUndREz+B1+Dbe/RGk4\n" + +            "MEqGFuOzrtsgEhPIX0hplhb0Tgz/rtug+yTT7oJjBa3u20AAOQ38/M99EfdeJvc4\n" + +            "lPFF1XBBLh6X9UKF72an2NuANiX6XPySnJgZ7nZ09RiYZqVwu/qt3DfvLfhboq+0\n" + +            "bQvLUPXrVDr70onv5UDjpmEA/cLmaIqqrduuTkFZOym65/PfAPvpGnt7crQj/Ibl\n" + +            "DEDYZQmP7AS+6zBjoOzNjUGE5r40zWAR1RSi7zliXTu+yfsjXUIhUAWmYR6J3KxB\n" + +            "lfsiHBQ+8dn9kC3YrUexWoOqBiqJOAJzZh5Y1tqgzfh+2nmHSB2dsQRs7rDRRlyy\n" + +            "YMbkpzL9ZsOUO2eTP1mmar6YjCN+rggYjRrX71K2SpBG6b1zZxOG+wIDAQABo2Aw\n" + +            "XjAdBgNVHQ4EFgQUuYGDLL2sswnYpHHvProt1JU+D48wDgYDVR0PAQH/BAQDAgIE\n" + +            "MAwGA1UdEwQFMAMBAf8wHwYDVR0jBBgwFoAUuYGDLL2sswnYpHHvProt1JU+D48w\n" + +            "DQYJKoZIhvcNAQENBQADggIBADeG67vaFcbITGpi51264kHPYPEWaXUa5XYbtmBl\n" + +            "cXYyB6hY5hv/YNuVGJ1gWsDmdeXEyj0j2icGQjYdHRfwhrbEri+h1EZOm1cSBDuY\n" + +            "k/P5+ctHyOXx8IE79DBsZ6IL61UKIaKhqZBfLGYcWu17DVV6+LT+AKtHhOrv3TSj\n" + +            "RnAcKnCbKqXLhUPXpK0eTjPYS2zQGQGIhIy9sQXVXJJJsGrPgMxna1Xw2JikBOCG\n" + +            "htD/JKwt6xBmNwktH0GI/LVtVgSp82Clbn9C4eZN9E5YbVYjLkIEDhpByeC71QhX\n" + +            "EIQ0ZR56bFuJA/CwValBqV/G9gscTPQqd+iETp8yrFpAVHOW+YzSFbxjTEkBte1J\n" + +            "aF0vmbqdMAWLk+LEFPQRptZh0B88igtx6tV5oVd+p5IVRM49poLhuPNJGPvMj99l\n" + +            "mlZ4+AeRUnbOOeAEuvpLJbel4rhwFzmUiGoeTVoPZyMevWcVFq6BMkS+jRR2w0jK\n" + +            "G6b0v5XDHlcFYPOgUrtsOBFJVwbutLvxdk6q37kIFnWCd8L3kmES5q4wjyFK47Co\n" + +            "Ja8zlx64jmMZPg/t3wWqkZgXZ14qnbyG5/lGsj5CwVtfDljrhN0oCWK1FZaUmW3d\n" + +            "69db12/g4f6phldhxiWuGC/W6fCW5kre7nmhshcltqAJJuU47iX+DarBFiIj816e\n" + +            "yV8e\n" + +            "-----END CERTIFICATE-----\n" + +            "\n" + +            "</ca>\n" + +            "<key>\n" + +            "-----BEGIN RSA PRIVATE KEY-----\n" + +            "MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDUTYWeGgsHS+fjijmziniNqw6h\n" + +            "MBpyK4S/cM6PxV28C33VuOWPTMcIYesctjZANWFCggfFTQSjV5Qaxq9UK4i27tayLbCdlVS6hpbl\n" + +            "Vf4DuI3Gj1Pv1rtITBShtvCf3T7yBnjW4wVpOpsUAAOViKUSvUU3kPPMFWhiGQw8yHYr82ts6XMo\n" + +            "jwMoonW5Ml4e7C7Cr22QesC63q7emNcpUd0pZGT9C33RgDAHZDMrlyjo4HEp1JbUfB0gbmXElJbE\n" + +            "1TNdZ62HhgmMjzTUN1GGrQ1t91AEoEQwaK65o4YSj+yFv6KXZZz5OWaz94tKiN9v26EXtBFmRlyb\n" + +            "6+D9ynSd9LghAgMBAAECggEBANPHLRXkhsHVj1EkzqBx7gXr8CEMmiTvknFh9zvltrZhhDoRQjWr\n" + +            "chPDkcRHY2Cznvy4N0YyqQDD2ULIlZdSAgPxxothFoBruWSD47yMBmLx08ORsDpcqt/YvPAATJI8\n" + +            "IpFNsXcyaXBp/M57oRemgnxp/8UJPJmFdWX99H4hvffh/jdj7POgYiWUaAl37XTYZKZ4nzKU2wpL\n" + +            "EDLj9RKPz9gG7CYp2zrLC9LaAsrXVrKwPBw6g+XwbClaqFj97db3mrY4lr6mTo89qmus1AU+fBDH\n" + +            "3Xlpmc8JwB+30TvhRNKrpLx9cEjuEj7K1gm8Y4dWCjPi+lNbtAyUBcgPJFa/81ECgYEA7pLoBU/Y\n" + +            "ZYjyHFca8FvDBcBh6haHfqJr9doXWtgjDrbi3o2n5wHqfKhFWOH6vPEQozkOVeX1ze6HOiRmGBpW\n" + +            "r+r7x8TD25L7I6HJw3M351RWOAfkF0w/RTVdetcTgduQtfN1u6BDhYSVceXMjyQYx7MhfETWI8Gh\n" + +            "KSYm8OEDYiUCgYEA489fmbrCcUnXzpTsbswJ5NmSoEXbcX8cLxnQuzE0z9GHhQdrMjOpXR76reTW\n" + +            "6jcuudarNcwRUYSWWhjCDKHhpx4HhasWPaHgr7jIzcRw8yZSJRSxKr8sl1qh6g7s47JcmfXOMWLt\n" + +            "yuyE933XrT19Th4ODZHY40Uv35mPjMi9d00CgYEAyRNAQtndBRa7GG/B4Ls2T+6pl+aNJIo4e+no\n" + +            "rURlp800wWabEPRocdBRQmyULBLxduBr2LIMzhgwGSz8b2wji/l9ZA3PFY135bxClVzSzUIjuO3N\n" + +            "rGUzHl2wAAyuAFDSUshzfkPBJRNt8aVBF5PQ3t93ZYmPAmv8LPZe875yX5ECgYEAsUEcwK/ZNW7g\n" + +            "dQPZR4iJNkC4Xu6cBZ6Cnn92swBheEYvLSoNlX0vDZ7aLE3/jzQqrjzC8NP8sbH5jtbuvgeDXZX3\n" + +            "AmGRp5j6C6A61ihAPmEVz3ZfN8SSfJ3vl//PAIg6lyz0J+cy4Q7RkwSeuVQ72Hl4M8TEvmmKC3Af\n" + +            "ispy6Y0CgYEAgl1o2lo+ACyk+oVQPaaPqK3d7WOBFp4eR2nXFor/vsx9igQOlZUgzRDQsR8jo1o9\n" + +            "efOSBf87igrZGgssys89pWa2dnXnz5PMmzkKr6bw4D9Ez6u6Puc9UZhGw/8wDYg6fSosdB9utspm\n" + +            "M698ycef7jBNMDgmhpSvfw5GctoNQ4s=\n" + +            "-----END RSA PRIVATE KEY-----\n" + +            "</key>\n" + +            "<cert>\n" + +            "-----BEGIN CERTIFICATE-----\n" + +            "MIIEjDCCAnSgAwIBAgIQG6MBp/cd9DlY+7cdvp3R3jANBgkqhkiG9w0BAQsFADBmMRAwDgYDVQQK\n" + +            "DAdCaXRtYXNrMRwwGgYDVQQLDBNodHRwczovL2JpdG1hc2submV0MTQwMgYDVQQDDCtCaXRtYXNr\n" + +            "IFJvb3QgQ0EgKGNsaWVudCBjZXJ0aWZpY2F0ZXMgb25seSEpMB4XDTE0MTIwNTAwMDAwMFoXDTE1\n" + +            "MDMwNTAwMDAwMFowLTErMCkGA1UEAwwiVU5MSU1JVEVEZDBwZDdkMzE4eTNtOHNkeXllaTFqYmZl\n" + +            "eDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANRNhZ4aCwdL5+OKObOKeI2rDqEwGnIr\n" + +            "hL9wzo/FXbwLfdW45Y9Mxwhh6xy2NkA1YUKCB8VNBKNXlBrGr1QriLbu1rItsJ2VVLqGluVV/gO4\n" + +            "jcaPU+/Wu0hMFKG28J/dPvIGeNbjBWk6mxQAA5WIpRK9RTeQ88wVaGIZDDzIdivza2zpcyiPAyii\n" + +            "dbkyXh7sLsKvbZB6wLrert6Y1ylR3SlkZP0LfdGAMAdkMyuXKOjgcSnUltR8HSBuZcSUlsTVM11n\n" + +            "rYeGCYyPNNQ3UYatDW33UASgRDBorrmjhhKP7IW/opdlnPk5ZrP3i0qI32/boRe0EWZGXJvr4P3K\n" + +            "dJ30uCECAwEAAaNvMG0wHQYDVR0OBBYEFK8bMVAM4GBB5sHptoIOAaIvlYueMAsGA1UdDwQEAwIH\n" + +            "gDATBgNVHSUEDDAKBggrBgEFBQcDAjAJBgNVHRMEAjAAMB8GA1UdIwQYMBaAFId+E7bsWFsUWah9\n" + +            "vZuPvZ7O+aJsMA0GCSqGSIb3DQEBCwUAA4ICAQAQOX81csVhvP422NKkZH7+g3npBpl+sEHedaGR\n" + +            "xYPOu4HrA4TVF9h44sljRoRJyenGNdBZCXcLKHg889eePTf8Z5K3lTojp6hvwyA6tgxOMHT1kESW\n" + +            "PfqnRw8mHfHJuE3g+4YNUMwggzwc/VZATdV/7M33sarVN9AUOHou9n9BizgCC+UnYlS+F2POumE3\n" + +            "FbOhKo5uubI02MwBYlN2JVO2TBt1Q20w8wc6cU07Xi5Epp+1mkgFiOShkNtPcJmEyBWJhxDtSDOW\n" + +            "2doqWYNqH2kq7B5R/kyyfcpFJqAnBTV7xs+C5rTS1mW7LpxfdCUMbYuLCpyxpO3A/DhAm8n47tUH\n" + +            "lBtmo8Avdb8VdFpYiGBpB0o9kTFcsWFb2GkWFBduGfSEB8jUI7QtqhgZqocAKK/cweSRV8FwyUcn\n" + +            "R0prRm3QEi9fbXqEddzjSY9y/lqWYzT7u+IOAQpKroeZ4wzgYperDNOUFuYk1rP7yuvjP2pV5rcN\n" + +            "yPoBP60TPVWMRM4WJm6nTogAz2qBrFsf/XwT/ajzbsjT6HNB7QbRE+wkFkqspoXG5Agp7KQ8lW3L\n" + +            "SKCDGOQJz7VIE85pD0tg7QEXBEw8oaRZtMjQ0Gvs25mxXAKka4wGasaWfYH6d0E+iKYcWn86V1rH\n" + +            "K2ZoknT+Nno5jgjFuUR3fZseNizEfx7BteooKQ==\n" + +            "-----END CERTIFICATE-----\n" + +            "</cert>\n" + +            "# crl-verify file missing in config profile\n" + +            "route 37.218.247.60  255.255.255.255 net_gateway\n"+ +            "remote-cert-tls server\n" + +            "data-ciphers AES-128-CBC\n" + +            "cipher AES-128-CBC\n" + +            "auth SHA1\n" + +            "persist-tun\n" + +            "# persist-tun also enables pre resolving to avoid DNS resolve problem\n" + +            "preresolve\n" + +            "# Use system proxy setting\n" + +            "management-query-proxy\n" +              "# Custom configuration options\n" +              "# You are on your on own here :)\n" +              "# These options found in the config file do not map to config settings:\n" + @@ -521,6 +650,8 @@ public class VpnConfigGeneratorTest {              "persist-tun\n" +              "# persist-tun also enables pre resolving to avoid DNS resolve problem\n" +              "preresolve\n" + +            "# Use system proxy setting\n" + +            "management-query-proxy\n" +              "# Custom configuration options\n" +              "# You are on your on own here :)\n" +              "# These options found in the config file do not map to config settings:\n" + @@ -640,6 +771,8 @@ public class VpnConfigGeneratorTest {              "persist-tun\n" +              "# persist-tun also enables pre resolving to avoid DNS resolve problem\n" +              "preresolve\n" + +            "# Use system proxy setting\n" + +            "management-query-proxy\n" +              "# Custom configuration options\n" +              "# You are on your on own here :)\n" +              "# These options found in the config file do not map to config settings:\n" + @@ -759,6 +892,8 @@ public class VpnConfigGeneratorTest {              "persist-tun\n" +              "# persist-tun also enables pre resolving to avoid DNS resolve problem\n" +              "preresolve\n" + +            "# Use system proxy setting\n" + +            "management-query-proxy\n" +              "# Custom configuration options\n" +              "# You are on your on own here :)\n" +              "# These options found in the config file do not map to config settings:\n" + @@ -882,6 +1017,8 @@ public class VpnConfigGeneratorTest {              "persist-tun\n" +              "# persist-tun also enables pre resolving to avoid DNS resolve problem\n" +              "preresolve\n" + +            "# Use system proxy setting\n" + +            "management-query-proxy\n" +              "# Custom configuration options\n" +              "# You are on your on own here :)\n" +              "# These options found in the config file do not map to config settings:\n" + @@ -1007,6 +1144,8 @@ public class VpnConfigGeneratorTest {              "persist-tun\n" +              "# persist-tun also enables pre resolving to avoid DNS resolve problem\n" +              "preresolve\n" + +            "# Use system proxy setting\n" + +            "management-query-proxy\n" +              "# Custom configuration options\n" +              "# You are on your on own here :)\n" +              "# These options found in the config file do not map to config settings:\n" + @@ -1140,6 +1279,8 @@ public class VpnConfigGeneratorTest {              "persist-tun\n" +              "# persist-tun also enables pre resolving to avoid DNS resolve problem\n" +              "preresolve\n" + +            "# Use system proxy setting\n" + +            "management-query-proxy\n" +              "# Custom configuration options\n" +              "# You are on your on own here :)\n" +              "# These options found in the config file do not map to config settings:\n" + @@ -1266,6 +1407,8 @@ public class VpnConfigGeneratorTest {              "persist-tun\n" +              "# persist-tun also enables pre resolving to avoid DNS resolve problem\n" +              "preresolve\n" + +            "# Use system proxy setting\n" + +            "management-query-proxy\n" +              "# Custom configuration options\n" +              "# You are on your on own here :)\n" +              "# These options found in the config file do not map to config settings:\n" + @@ -1286,78 +1429,92 @@ public class VpnConfigGeneratorTest {          mockStatic(PreferenceManager.class);          SharedPreferences preferences = mock(SharedPreferences.class, RETURNS_DEEP_STUBS);          when(PreferenceManager.getDefaultSharedPreferences(any(Context.class))).thenReturn(preferences); +        when(preferences.getBoolean("usesystemproxy", true)).thenReturn(true);          when(context.getCacheDir()).thenReturn(new File("/data/data/se.leap.bitmask")); - +        mockStatic(ConfigHelper.ObfsVpnHelper.class);      }      @Test      public void testGenerateVpnProfile_v1_tcp_udp() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("gateway_tcp_udp.json"))); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 1, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 1, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertFalse(vpnProfiles.containsKey(OBFS4)); -        assertTrue(vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim().equals(expectedVPNConfig_v1_tcp_udp.trim())); +        assertEquals(expectedVPNConfig_v1_tcp_udp.trim(), vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim());      }      @Test      public void testGenerateVpnProfile_v1_udp_tcp() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("gateway_udp_tcp.json"))); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 1, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 1, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertFalse(vpnProfiles.containsKey(OBFS4)); -        assertTrue(vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim().equals(expectedVPNConfig_v1_udp_tcp.trim())); +        assertEquals(expectedVPNConfig_v1_udp_tcp.trim(), vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim());      }      @Test      public void testGenerateVpnProfile_v2_tcp_udp() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("gateway_tcp_udp.json"))); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 2, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 2, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertFalse(vpnProfiles.containsKey(OBFS4)); -        assertTrue(vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim().equals(expectedVPNConfig_v1_tcp_udp.trim())); +        assertEquals(expectedVPNConfig_v1_tcp_udp.trim(), vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim());      }      @Test      public void testGenerateVpnProfile_v2_udp_tcp() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("gateway_udp_tcp.json"))); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 2, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 2, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertFalse(vpnProfiles.containsKey(OBFS4)); -        assertTrue(vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim().equals(expectedVPNConfig_v1_udp_tcp.trim())); +        assertEquals(expectedVPNConfig_v1_udp_tcp.trim(), vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim());      }      @Test      public void testGenerateVpnProfile_v3_obfs4() throws Exception { +        when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(false);          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo.bitmask.eip-service.json"))).getJSONArray("gateways").getJSONObject(0); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertTrue(vpnProfiles.containsKey(OBFS4));          assertTrue(vpnProfiles.containsKey(OPENVPN));          System.out.println(vpnProfiles.get(OBFS4).getConfigFile(context, false)); -        assertTrue(vpnProfiles.get(OBFS4).getConfigFile(context, false).trim().equals(expectedVPNConfig_v3_obfs4.trim())); +        assertEquals(expectedVPNConfig_v3_obfs4.trim(), vpnProfiles.get(OBFS4).getConfigFile(context, false).trim()); +    } + +    @Test +    public void testGenerateVpnProfile_v3_obfs4_obfsvpn() throws Exception { +        when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); +        gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo.bitmask.eip-service.json"))).getJSONArray("gateways").getJSONObject(0); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false, false); +        HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); +        assertTrue(vpnProfiles.containsKey(OBFS4)); +        assertTrue(vpnProfiles.containsKey(OPENVPN)); +        System.out.println(vpnProfiles.get(OBFS4).getConfigFile(context, false)); +        assertEquals(expectedVPNConfig_v3_obfsvpn_obfs4.trim(), vpnProfiles.get(OBFS4).getConfigFile(context, false).trim());      }      @Test      public void testGenerateVpnProfile_v3_ovpn_tcp_udp() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_pt_tcp_udp.eip-service.json"))).getJSONArray("gateways").getJSONObject(0); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertTrue(vpnProfiles.containsKey(OBFS4));          assertTrue(vpnProfiles.containsKey(OPENVPN));          System.out.println(vpnProfiles.get(OPENVPN).getConfigFile(context, false)); -        assertTrue(vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim().equals(expectedVPNConfig_v3_ovpn_tcp_udp.trim())); +        assertEquals(expectedVPNConfig_v3_ovpn_tcp_udp.trim(), vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim());      }      @Test      public void testGenerateVpnProfile_v3_ovpn_udp_tcp() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_pt_udp_tcp.eip-service.json"))).getJSONArray("gateways").getJSONObject(0); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertTrue(vpnProfiles.containsKey(OBFS4));          assertTrue(vpnProfiles.containsKey(OPENVPN));          System.out.println(vpnProfiles.get(OPENVPN).getConfigFile(context, false)); -        assertTrue(vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim().equals(expectedVPNConfig_v3_ovpn_udp_tcp.trim())); +        assertEquals(expectedVPNConfig_v3_ovpn_udp_tcp.trim(), vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim());      }      @Test @@ -1365,44 +1522,43 @@ public class VpnConfigGeneratorTest {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_pt_udp_tcp.eip-service.json"))).getJSONArray("gateways").getJSONObject(0);          //delete "data-ciphers" from config to test if the resulting openvpn config file will contain the default value taken from "cipher" flag          generalConfig.put("data-ciphers", null); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertTrue(vpnProfiles.containsKey(OBFS4));          assertTrue(vpnProfiles.containsKey(OPENVPN));          System.out.println(vpnProfiles.get(OPENVPN).getConfigFile(context, false)); -        assertTrue(vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim().equals(expectedVPNConfig_v3_ovpn_udp_tcp_defaultDataCiphers.trim())); +        assertEquals(expectedVPNConfig_v3_ovpn_udp_tcp_defaultDataCiphers.trim(), vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim());      }      @Test      public void testGenerateVpnProfile_v4_ovpn_tcp_udp() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("v4/ptdemo_pt_tcp_udp.eip-service.json"))).getJSONArray("gateways").getJSONObject(0);          generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("v4/ptdemo_pt_tcp_udp.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 4, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 4, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertTrue(vpnProfiles.containsKey(OBFS4));          assertTrue(vpnProfiles.containsKey(OPENVPN));          System.out.println(vpnProfiles.get(OPENVPN).getConfigFile(context, false)); -        assertTrue(vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim().equals(expectedVPNConfig_v4_ovpn_tcp_udp.trim())); +        assertEquals(expectedVPNConfig_v4_ovpn_tcp_udp.trim(), vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim());      }      @Test      public void testGenerateVpnProfile_v4_ovpn_udp_tcp() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("v4/ptdemo_pt_udp_tcp.eip-service.json"))).getJSONArray("gateways").getJSONObject(0);          generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("v4/ptdemo_pt_udp_tcp.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 4, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 4, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertTrue(vpnProfiles.containsKey(OBFS4));          assertTrue(vpnProfiles.containsKey(OPENVPN));          System.out.println(vpnProfiles.get(OPENVPN).getConfigFile(context, false)); -        System.out.println(expectedVPNConfig_v4_ovpn_udp_tcp.trim()); -        assertTrue(vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim().equals(expectedVPNConfig_v4_ovpn_udp_tcp.trim())); +        assertEquals(expectedVPNConfig_v4_ovpn_udp_tcp.trim(), vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim());      }      @Test      public void testGenerateVpnProfile_v3_ipv6only_allowOpenvpnIPv6Only() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_misconfigured_ipv6.json"))).getJSONArray("gateways").getJSONObject(0);          generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_misconfigured_ipv6.json"))).getJSONObject(OPENVPN_CONFIGURATION); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertTrue(vpnProfiles.containsKey(OPENVPN));      } @@ -1411,7 +1567,7 @@ public class VpnConfigGeneratorTest {      public void testGenerateVpnProfile_v3_obfs4IPv6_skip() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_misconfigured_ipv6.json"))).getJSONArray("gateways").getJSONObject(0);          generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_misconfigured_ipv6.json"))).getJSONObject(OPENVPN_CONFIGURATION); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertFalse(vpnProfiles.containsKey(OBFS4));      } @@ -1423,7 +1579,7 @@ public class VpnConfigGeneratorTest {      public void testGenerateVpnProfile_v3_obfs4IPv4AndIPv6_skipIPv6() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_misconfigured_ipv4ipv6.json"))).getJSONArray("gateways").getJSONObject(0);          generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_misconfigured_ipv4ipv6.json"))).getJSONObject(OPENVPN_CONFIGURATION); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertTrue(vpnProfiles.containsKey(OBFS4));          assertTrue(vpnProfiles.containsKey(OPENVPN)); @@ -1438,7 +1594,7 @@ public class VpnConfigGeneratorTest {      public void testGenerateVpnProfile_v3_obfs4udp_skip() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_misconfigured_udp.json"))).getJSONArray("gateways").getJSONObject(0);          generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_misconfigured_udp.json"))).getJSONObject(OPENVPN_CONFIGURATION); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertFalse(vpnProfiles.containsKey(OBFS4));          assertTrue(vpnProfiles.containsKey(OPENVPN)); @@ -1448,7 +1604,7 @@ public class VpnConfigGeneratorTest {      public void testGenerateVpnProfile_v3_obfs4UDPAndTCP_skipUDP() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_misconfigured_udptcp.json"))).getJSONArray("gateways").getJSONObject(0);          generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_misconfigured_udptcp.json"))).getJSONObject(OPENVPN_CONFIGURATION); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertTrue(vpnProfiles.containsKey(OBFS4));          assertTrue(vpnProfiles.containsKey(OPENVPN)); @@ -1460,23 +1616,33 @@ public class VpnConfigGeneratorTest {      public void testGenerateVpnProfile_preferUDP_firstRemotesUDP() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("v4/multiport_tcpudp_eip-service.json"))).getJSONArray("gateways").getJSONObject(0);          generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("v4/multiport_tcpudp_eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, true); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, true, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          assertTrue(vpnProfiles.containsKey(OBFS4));          assertTrue(vpnProfiles.containsKey(OPENVPN));          System.out.println(vpnProfiles.get(OPENVPN).getConfigFile(context, false)); -        System.out.println(expectedVPNConfig_v4_ovpn_multiport_tcpudp.trim()); -        assertTrue(vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim().equals(expectedVPNConfig_v4_ovpn_multiport_tcpudp.trim())); +        assertEquals(expectedVPNConfig_v4_ovpn_multiport_tcpudp.trim(), vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim());      }      @Test      public void testGenerateVpnProfile_testNewCiphers() throws Exception {          gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("v4/ptdemo_pt_tcp_udp_new_ciphers.eip-service.json"))).getJSONArray("gateways").getJSONObject(0);          generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("v4/ptdemo_pt_tcp_udp_new_ciphers.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); -        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 4, false); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 4, false, false);          HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles();          System.out.println(vpnProfiles.get(OPENVPN).getConfigFile(context, false)); -        assertTrue(vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim().equals(expectedVPNConfig_v4_ovpn_tcp_udp_new_ciphers.trim())); +        assertEquals(expectedVPNConfig_v4_ovpn_tcp_udp_new_ciphers.trim(), vpnProfiles.get(OPENVPN).getConfigFile(context, false).trim()); +    } + +    public void testGenerateVpnProfileExperimentalTransportsEnabled () throws Exception { +        gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_kcp_gateways.json"))).getJSONArray("gateways").getJSONObject(0); +        generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_kcp_gateways.json"))).getJSONObject(OPENVPN_CONFIGURATION); +        vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, 3, true, true); +        HashMap<Connection.TransportType, VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); +        assertTrue(vpnProfiles.containsKey(OBFS4)); +        assertTrue(vpnProfiles.containsKey(OBFS4_KCP)); +        assertTrue(vpnProfiles.containsKey(OPENVPN)); +      }  }
\ No newline at end of file diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java index 61d42f58..a455a4bf 100644 --- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java @@ -36,7 +36,6 @@ import org.json.JSONException;  import org.json.JSONObject;  import org.mockito.invocation.InvocationOnMock;  import org.mockito.stubbing.Answer; -import org.powermock.api.mockito.PowerMockito;  import java.io.File;  import java.io.FileNotFoundException; diff --git a/app/src/test/resources/ptdemo_kcp_gateways.json b/app/src/test/resources/ptdemo_kcp_gateways.json new file mode 100644 index 00000000..332c8c11 --- /dev/null +++ b/app/src/test/resources/ptdemo_kcp_gateways.json @@ -0,0 +1,160 @@ +{ +  "gateways":[ +    { +      "capabilities":{ +        "adblock":false, +        "filter_dns":false, +        "limited":false, +        "transport":[ +          { +            "type":"obfs4", +            "protocols":[ +              "tcp" +            ], +            "ports":[ +              "23049" +            ], +            "options": { +              "cert": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1", +              "iatMode": "0" +            } +          }, +          { +            "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":"obfs4", +            "protocols":[ +              "tcp" +            ], +            "ports":[ +              "443" +            ], +            "options": { +              "cert": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX2", +              "iatMode": "0" +            } +          }, +          { +            "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 diff --git a/app/src/test/resources/ptdemo_kcp_gateways_geoip.json b/app/src/test/resources/ptdemo_kcp_gateways_geoip.json new file mode 100644 index 00000000..9ac7257d --- /dev/null +++ b/app/src/test/resources/ptdemo_kcp_gateways_geoip.json @@ -0,0 +1,29 @@ +{ +  "ip":"51.158.144.32", +  "cc":"FR", +  "city":"Paris", +  "lat":48.8628, +  "lon":2.3292, +  "gateways":[ +    "pt.demo.bitmask.net", +    "moscow.bitmask.net", +    "manila.bitmask.net" +  ], +  "sortedGateways": [ +    { +      "host": "pt.demo.bitmask.net", +      "fullness": 0.3, +      "overload": false +    }, +    { +      "host": "moscow.bitmask.net", +      "fullness": 0.36, +      "overload": false +    }, +    { +      "host": "manila.bitmask.net", +      "fullness": 0.59, +      "overload": false +    } +  ] +}
\ No newline at end of file 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 diff --git a/bitmaskcore b/bitmaskcore -Subproject 5e5b3ebebc5437e83f3bc5f0afce46b6b4aa465 +Subproject a63cd6b2c573edb1de3119f4a158f840e4c4a9b diff --git a/lib-bitmask-core-arm64/bitmaskcore_arm64-sources.jar b/lib-bitmask-core-arm64/bitmaskcore_arm64-sources.jarBinary files differ deleted file mode 100644 index 5b475b1f..00000000 --- a/lib-bitmask-core-arm64/bitmaskcore_arm64-sources.jar +++ /dev/null diff --git a/lib-bitmask-core-arm64/bitmaskcore_arm64.aar b/lib-bitmask-core-arm64/bitmaskcore_arm64.aarBinary files differ deleted file mode 100644 index 5a12115c..00000000 --- a/lib-bitmask-core-arm64/bitmaskcore_arm64.aar +++ /dev/null diff --git a/lib-bitmask-core-armv7/bitmaskcore_arm-sources.jar b/lib-bitmask-core-armv7/bitmaskcore_arm-sources.jarBinary files differ deleted file mode 100644 index 5b475b1f..00000000 --- a/lib-bitmask-core-armv7/bitmaskcore_arm-sources.jar +++ /dev/null diff --git a/lib-bitmask-core-armv7/bitmaskcore_arm.aar b/lib-bitmask-core-armv7/bitmaskcore_arm.aarBinary files differ deleted file mode 100644 index d947ed88..00000000 --- a/lib-bitmask-core-armv7/bitmaskcore_arm.aar +++ /dev/null diff --git a/lib-bitmask-core-web/bitmaskcore_web-sources.jar b/lib-bitmask-core-web/bitmaskcore_web-sources.jarBinary files differ deleted file mode 100644 index 9bbe2dff..00000000 --- a/lib-bitmask-core-web/bitmaskcore_web-sources.jar +++ /dev/null diff --git a/lib-bitmask-core-web/bitmaskcore_web.aar b/lib-bitmask-core-web/bitmaskcore_web.aarBinary files differ deleted file mode 100644 index b5c931d6..00000000 --- a/lib-bitmask-core-web/bitmaskcore_web.aar +++ /dev/null diff --git a/lib-bitmask-core-x86/bitmaskcore_x86-sources.jar b/lib-bitmask-core-x86/bitmaskcore_x86-sources.jarBinary files differ deleted file mode 100644 index 5b475b1f..00000000 --- a/lib-bitmask-core-x86/bitmaskcore_x86-sources.jar +++ /dev/null diff --git a/lib-bitmask-core-x86/bitmaskcore_x86.aar b/lib-bitmask-core-x86/bitmaskcore_x86.aarBinary files differ deleted file mode 100644 index eb0ae963..00000000 --- a/lib-bitmask-core-x86/bitmaskcore_x86.aar +++ /dev/null diff --git a/lib-bitmask-core-x86_64/bitmaskcore_x86_64-sources.jar b/lib-bitmask-core-x86_64/bitmaskcore_x86_64-sources.jarBinary files differ deleted file mode 100644 index 5b475b1f..00000000 --- a/lib-bitmask-core-x86_64/bitmaskcore_x86_64-sources.jar +++ /dev/null diff --git a/lib-bitmask-core-x86_64/bitmaskcore_x86_64.aar b/lib-bitmask-core-x86_64/bitmaskcore_x86_64.aarBinary files differ deleted file mode 100644 index 7f428f45..00000000 --- a/lib-bitmask-core-x86_64/bitmaskcore_x86_64.aar +++ /dev/null diff --git a/lib-bitmask-core/bitmaskcore-sources.jar b/lib-bitmask-core/bitmaskcore-sources.jarBinary files differ deleted file mode 100644 index 5b475b1f..00000000 --- a/lib-bitmask-core/bitmaskcore-sources.jar +++ /dev/null diff --git a/lib-bitmask-core/bitmaskcore.aar b/lib-bitmask-core/bitmaskcore.aarBinary files differ deleted file mode 100644 index fe28f633..00000000 --- a/lib-bitmask-core/bitmaskcore.aar +++ /dev/null diff --git a/scripts/build_deps.sh b/scripts/build_deps.sh index 83fed5e8..14d2bafc 100755 --- a/scripts/build_deps.sh +++ b/scripts/build_deps.sh @@ -15,7 +15,8 @@ DIR_GOLIBS=./bitmaskcore/lib/  DIR_TORLIBS=./tor-android/external/lib  EXPECTED_NDK_VERSION="21.4.7075529"  EXPECTED_ANDROID_NDK_RELEASE_VERSION="r21e" -BUILD_TOR=true +if [[ -z $BUILD_TOR ]]; then BUILD_TOR=true; fi +if [[ -z $BUILD_OPENVPN_LIBS ]]; then BUILD_OPENVPN_LIBS=true; fi  # init  # look for empty dir @@ -60,8 +61,9 @@ else  fi  # build openvpn libs -if [[ $(ls -A ${DIR_OVPNASSETS}) && $(ls -A ${DIR_OVPNLIBS}) ]] -then +if [[ ${BUILD_OPENVPN_LIBS} == false ]]; then +  echo "skipping openvpn" +elif [[ $(ls -A ${DIR_OVPNASSETS}) && $(ls -A ${DIR_OVPNLIBS}) ]]; then      echo "Dirty build: skipped externalNativeBuild - reusing existing libs"  else      echo "Clean build: starting externalNativeBuild" diff --git a/scripts/prepareForTests.sh b/scripts/prepareForTests.sh new file mode 100755 index 00000000..d10a04b7 --- /dev/null +++ b/scripts/prepareForTests.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Copyright (c) 2022 LEAP Encryption Access Project and contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +SCRIPT_DIR=$(dirname "$0") +BASE_DIR="$SCRIPT_DIR/.." + +git checkout -- \* +git checkout -- \.\* +rm -r $BASE_DIR/bitmaskcore/lib/* +git submodule foreach --recursive git reset --hard HEAD +git submodule sync --recursive +git submodule update --init --recursive + +BUILD_TOR=false BUILD_OPENVPN_LIBS=false ./scripts/build_deps.sh
\ No newline at end of file | 
