summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcyBerta <cyberta@riseup.net>2022-07-15 16:31:48 +0200
committercyBerta <cyberta@riseup.net>2022-07-19 00:06:12 +0200
commit95c923f2850ac409e8413cc4902c69848c64d7c8 (patch)
tree8c5bb3cdfde0719449e3f9ea3293bda3d975d99e
parent1f12d1b99a88bf62f29798a30135473299978234 (diff)
Parse different obfs4 flavors from eip-service.json. In the gateway load / gateway selection UI all pluggable transports flavors will be summed up and handled the same way. A gateway can support both obfs4 and the kcp flavor.
-rw-r--r--app/src/main/java/de/blinkt/openvpn/VpnProfile.java27
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java10
-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/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java6
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java1
-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/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.java44
14 files changed, 272 insertions, 137 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 94922ed5..4b394136 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
@@ -317,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,
@@ -416,7 +416,7 @@ 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 (useObfsVpn()) {
if (obfsVpnClient != null && obfsVpnClient.isStarted()) {
@@ -1033,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,
@@ -1064,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,
@@ -1108,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/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/models/Constants.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
index d7a54fcc..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";
//////////////////////////////////////////////
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/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 62c267ac..58732713 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
@@ -17,6 +17,7 @@
package se.leap.bitmaskclient.eip;
import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4;
+import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_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;
@@ -55,28 +56,30 @@ import se.leap.bitmaskclient.base.utils.ConfigHelper;
import se.leap.bitmaskclient.pluggableTransports.Obfs4Options;
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);
@@ -84,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;
}
}
}
@@ -108,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;
}
@@ -115,6 +129,10 @@ public class VpnConfigGenerator {
return obfs4Transport != null;
}
+ private boolean supportsObfs4Kcp() {
+ return obfs4TKcpTransport != null;
+ }
+
private String getConfigurationString(Connection.TransportType transportType) {
return generalConfiguration()
+ newLine
@@ -131,18 +149,20 @@ 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);
- boolean udp = false;
+ boolean udp = useUdp;
if (BuildConfig.obfsvpn_pinning) {
cert = BuildConfig.obfsvpn_cert;