summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
authorcyberta <cyberta@riseup.net>2022-07-19 17:26:25 +0000
committercyberta <cyberta@riseup.net>2022-07-19 17:26:25 +0000
commit6bc3a79e6d09a8fa1857f1d502a87c1c0633be35 (patch)
tree7aa914e63bb6aae27c8521dd198e3f7416e9890c /app/src
parentabd8c8d06f01cc5b794a18779c2ea36b9317b4e3 (diff)
parent4a95fbad79333bc1cc83b22c8122293a3d8d332f (diff)
Merge branch 'integrate_obfsvpn' into 'master'
update bitmaskcore, integrating obfsvpn See merge request leap/bitmask_android!190
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/java/de/blinkt/openvpn/VpnProfile.java27
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java39
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/connection/Connection.java16
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4Connection.java26
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java6
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java1
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java21
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Location.java19
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Pair.java57
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java27
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java7
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java46
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/views/SelectLocationEntry.java3
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java83
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java37
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java58
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java109
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Obfs4Options.java5
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java127
-rw-r--r--app/src/main/res/layout/f_settings.xml9
-rw-r--r--app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java120
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/base/models/ProviderTest.java83
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java94
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java2
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java258
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java1
-rw-r--r--app/src/test/resources/ptdemo_kcp_gateways.json160
-rw-r--r--app/src/test/resources/ptdemo_kcp_gateways_geoip.json29
-rw-r--r--app/src/test/resources/ptdemo_only_experimental_transports_gateways.json134
31 files changed, 1333 insertions, 275 deletions
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