From f6017ab12d0c472ab4f22e81d9a768ad2510b134 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 31 Mar 2023 15:46:09 +0200 Subject: don't handle obfs4 over kcp as a separate pluggable transport, instead 'tcp' and 'kcp' become valid protocols for obfs4 --- .../main/java/de/blinkt/openvpn/VpnProfile.java | 25 ++++------- .../blinkt/openvpn/core/connection/Connection.java | 21 +++++++-- .../leap/bitmaskclient/base/models/Provider.java | 23 ++++++---- .../main/java/se/leap/bitmaskclient/eip/EIP.java | 17 +++---- .../java/se/leap/bitmaskclient/eip/Gateway.java | 7 ++- .../se/leap/bitmaskclient/eip/GatewaysManager.java | 33 ++++++++------ .../leap/bitmaskclient/eip/VpnConfigGenerator.java | 36 ++++----------- .../java/de/blinkt/openvpn/VpnProfileTest.java | 11 +++-- .../bitmaskclient/eip/GatewaysManagerTest.java | 52 +++++++++++----------- .../bitmaskclient/eip/VpnConfigGeneratorTest.java | 29 ++++++------ app/src/test/resources/ptdemo_kcp_gateways.json | 17 +------ .../ptdemo_misconfigured_kcp_gateways.json | 4 +- ...demo_only_experimental_transports_gateways.json | 8 ++-- 13 files changed, 138 insertions(+), 145 deletions(-) (limited to 'app/src') diff --git a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java index 83cde85e..3428e0c4 100644 --- a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java +++ b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java @@ -5,6 +5,11 @@ package de.blinkt.openvpn; +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.PROVIDER_PROFILE; +import static se.leap.bitmaskclient.base.utils.ConfigHelper.stringEqual; + import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; @@ -34,8 +39,6 @@ import java.io.FileWriter; import java.io.IOException; import java.io.Serializable; import java.io.StringWriter; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; @@ -69,12 +72,6 @@ import de.blinkt.openvpn.core.connection.ConnectionAdapter; 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; - public class VpnProfile implements Serializable, Cloneable { // Note that this class cannot be moved to core where it belongs since // the profile loading depends on it being here @@ -184,6 +181,7 @@ public class VpnProfile implements Serializable, Cloneable { /* Options no longer used in new profiles */ public String mServerName = "openvpn.example.com"; public String mServerPort = "1194"; + @Deprecated public boolean mUseUdp = true; public boolean mTemporaryProfile = false; private transient PrivateKey mPrivateKey; @@ -193,8 +191,7 @@ public class VpnProfile implements Serializable, Cloneable { private int mProfileVersion; public boolean mBlockUnusedAddressFamilies = true; public String mGatewayIp; - private boolean mUseObfs4; - private boolean mUseObfs4Kcp; + private final boolean mUseObfs4; public VpnProfile(String name, Connection.TransportType transportType) { mUuid = UUID.randomUUID(); @@ -204,7 +201,6 @@ public class VpnProfile implements Serializable, Cloneable { mConnections = new Connection[1]; mLastUsed = System.currentTimeMillis(); mUseObfs4 = transportType == OBFS4; - mUseObfs4Kcp = transportType == OBFS4_KCP; } public static String openVpnEscape(String unescaped) { @@ -270,8 +266,7 @@ public class VpnProfile implements Serializable, Cloneable { if (obj instanceof VpnProfile) { VpnProfile vp = (VpnProfile) obj; return stringEqual(vp.mGatewayIp, mGatewayIp) && - vp.mUseObfs4 == mUseObfs4 && - vp.mUseObfs4Kcp == mUseObfs4Kcp; + vp.mUseObfs4 == mUseObfs4; } return false; } @@ -302,14 +297,12 @@ public class VpnProfile implements Serializable, Cloneable { } public boolean usePluggableTransports() { - return mUseObfs4Kcp || mUseObfs4; + return mUseObfs4; } public Connection.TransportType getTransportType() { if (mUseObfs4) { return OBFS4; - } else if (mUseObfs4Kcp) { - return OBFS4_KCP; } else { return OPENVPN; } 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 f60e7333..137b3f16 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 @@ -37,10 +37,25 @@ public abstract class Connection implements Serializable, Cloneable { ORBOT } + public enum TransportProtocol { + UDP("udp"), + TCP("tcp"), + KCP("kcp"); + + String protocol; + + TransportProtocol(String transportProtocol) { + this.protocol = transportProtocol; + } + + @Override + public String toString() { + return protocol; + } + } public enum TransportType { OBFS4("obfs4"), OPENVPN("openvpn"), - OBFS4_KCP("obfs4-1"), PT("metaTransport"); @@ -56,11 +71,11 @@ public abstract class Connection implements Serializable, Cloneable { } public boolean isPluggableTransport() { - return this == OBFS4 || this == OBFS4_KCP || this == PT; + return this == OBFS4 || this == PT; } public TransportType getMetaType() { - if (this == OBFS4 || this == OBFS4_KCP || this == PT) { + if (this == OBFS4 || this == PT) { return PT; } return OPENVPN; 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 53f864cf..62fb1fd2 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 @@ -16,11 +16,13 @@ */ package se.leap.bitmaskclient.base.models; +import static de.blinkt.openvpn.core.connection.Connection.TransportProtocol.KCP; +import static de.blinkt.openvpn.core.connection.Connection.TransportProtocol.TCP; 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; +import static se.leap.bitmaskclient.base.models.Constants.PROTOCOLS; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_ALLOWED_REGISTERED; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_ALLOW_ANONYMOUS; import static se.leap.bitmaskclient.base.models.Constants.TRANSPORT; @@ -46,6 +48,7 @@ import java.util.HashSet; import java.util.Locale; import java.util.Set; +import de.blinkt.openvpn.core.connection.Connection.TransportProtocol; import de.blinkt.openvpn.core.connection.Connection.TransportType; import motd.IStringCollection; import motd.Motd; @@ -181,16 +184,16 @@ public final class Provider implements Parcelable { public boolean supportsPluggableTransports() { if (useObfsVpn()) { - return supportsTransports(new TransportType[]{OBFS4, OBFS4_KCP}); + return supportsTransports(new Pair[]{new Pair<>(OBFS4, TCP), new Pair<>(OBFS4, KCP)}); } - return supportsTransports(new TransportType[]{OBFS4}); + return supportsTransports(new Pair[]{new Pair<>(OBFS4, TCP)}); } public boolean supportsExperimentalPluggableTransports() { - return supportsTransports(new TransportType[]{OBFS4_KCP}); + return supportsTransports(new Pair[]{new Pair<>(OBFS4, KCP)}); } - private boolean supportsTransports(TransportType[] transportTypes) { + private boolean supportsTransports(Pair[] transportTypes) { try { JSONArray gatewayJsons = eipServiceJson.getJSONArray(GATEWAYS); for (int i = 0; i < gatewayJsons.length(); i++) { @@ -199,9 +202,13 @@ public final class Provider implements Parcelable { getJSONArray(TRANSPORT); for (int j = 0; j < transports.length(); j++) { String supportedTransportType = transports.getJSONObject(j).getString(TYPE); - for (TransportType transportType : transportTypes) { - if (transportType.toString().equals(supportedTransportType)) { - return true; + JSONArray transportProtocols = transports.getJSONObject(j).getJSONArray(PROTOCOLS); + for (Pair transportPair : transportTypes) { + for (int k = 0; k < transportProtocols.length(); k++) { + if (transportPair.first.toString().equals(supportedTransportType) && + transportPair.second.toString().equals(transportProtocols.getString(k))) { + return true; + } } } } 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 88cdc715..5b082448 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -91,6 +91,7 @@ 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 se.leap.bitmaskclient.eip.GatewaysManager.GatewayOptions; /** * EIP is the abstract base class for interacting with and managing the Encrypted @@ -255,8 +256,8 @@ public final class EIP extends JobIntentService implements Observer { return; } - Pair gatewayTransportTypePair = gatewaysManager.select(nClosestGateway); - launchActiveGateway(gatewayTransportTypePair, nClosestGateway, result); + GatewayOptions gatewayOptions = gatewaysManager.select(nClosestGateway); + launchActiveGateway(gatewayOptions, nClosestGateway, result); if (result.containsKey(BROADCAST_RESULT_KEY) && !result.getBoolean(BROADCAST_RESULT_KEY)) { tellToReceiverOrBroadcast(this, EIP_ACTION_START, RESULT_CANCELED, result); } else { @@ -270,7 +271,7 @@ public final class EIP extends JobIntentService implements Observer { */ private void startEIPAlwaysOnVpn() { GatewaysManager gatewaysManager = new GatewaysManager(getApplicationContext()); - Pair gatewayTransportTypePair = gatewaysManager.select(0); + GatewayOptions gatewayOptions = gatewaysManager.select(0); Bundle result = new Bundle(); if (shouldUpdateVPNCertificate()) { @@ -279,7 +280,7 @@ public final class EIP extends JobIntentService implements Observer { ProviderObservable.getInstance().updateProvider(p); } - launchActiveGateway(gatewayTransportTypePair, 0, result); + launchActiveGateway(gatewayOptions, 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)); } @@ -323,13 +324,13 @@ public final class EIP extends JobIntentService implements Observer { /** * starts the VPN and connects to the given gateway * - * @param gatewayTransportTypePair Pair of Gateway and associated transport used to connect + * @param gatewayOptions GatewayOptions model containing a Gateway and the associated transport used to connect */ - private void launchActiveGateway(@Nullable Pair gatewayTransportTypePair, int nClosestGateway, Bundle result) { + private void launchActiveGateway(@Nullable GatewayOptions gatewayOptions, int nClosestGateway, Bundle result) { VpnProfile profile; - if (gatewayTransportTypePair == null || gatewayTransportTypePair.first == null || - (profile = gatewayTransportTypePair.first.getProfile(gatewayTransportTypePair.second)) == null) { + if (gatewayOptions == null || gatewayOptions.gateway == null || + (profile = gatewayOptions.gateway.getProfile(gatewayOptions.transportType)) == 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 929935eb..719b960e 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,7 @@ */ package se.leap.bitmaskclient.eip; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.PT; 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; @@ -52,7 +53,6 @@ import java.util.HashSet; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ConfigParser; import de.blinkt.openvpn.core.connection.Connection; -import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.utils.ConfigHelper; /** @@ -77,6 +77,9 @@ public class Gateway { private String name; private int timezone; private int apiVersion; + /** FIXME: We expect here that not more than one obfs4 transport is offered by a gateway, however + * it's possible to setup gateways that have obfs4 over kcp and tcp which result in different VpnProfiles each + */ private HashMap vpnProfiles; /** @@ -209,7 +212,7 @@ public class Gateway { } public boolean supportsTransport(Connection.TransportType transportType) { - if (transportType == Connection.TransportType.PT) { + if (transportType == PT) { return supportsPluggableTransports(); } return vpnProfiles.get(transportType) != null; 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 521d095e..28766998 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -17,7 +17,6 @@ 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 de.blinkt.openvpn.core.connection.Connection.TransportType.PT; import static se.leap.bitmaskclient.base.models.Constants.GATEWAYS; @@ -61,7 +60,6 @@ import se.leap.bitmaskclient.BuildConfig; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.models.GatewayJson; 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.models.Transport; @@ -103,6 +101,16 @@ public class GatewaysManager { } } + public static class GatewayOptions { + public Gateway gateway; + public TransportType transportType; + + public GatewayOptions(Gateway gateway, TransportType transportType) { + this.gateway = gateway; + this.transportType = transportType; + } + } + private static final String TAG = GatewaysManager.class.getSimpleName(); public static final String PINNED_OBFUSCATION_PROXY = "pinned.obfuscation.proxy"; @@ -122,7 +130,7 @@ public class GatewaysManager { * select closest Gateway * @return the n closest Gateway */ - public Pair select(int nClosest) { + public GatewayOptions select(int nClosest) { if (PreferenceHelper.useObfuscationPinning(context)) { if (nClosest > 2) { // no need to try again the pinned proxy, probably configuration error @@ -132,14 +140,14 @@ public class GatewaysManager { if (gateway == null) { return null; } - return new Pair<>(gateway, getObfuscationPinningKCP(context) ? OBFS4_KCP : OBFS4); + return new GatewayOptions(gateway, OBFS4); } String selectedCity = getPreferredCity(context); return select(nClosest, selectedCity); } - public Pair select(int nClosest, String city) { - TransportType[] transportTypes = getUseBridges(context) ? new TransportType[]{OBFS4, OBFS4_KCP} : new TransportType[]{OPENVPN}; + public GatewayOptions select(int nClosest, String city) { + TransportType[] transportTypes = getUseBridges(context) ? new TransportType[]{OBFS4} : new TransportType[]{OPENVPN}; if (presortedList.size() > 0) { return getGatewayFromPresortedList(nClosest, transportTypes, city); } @@ -266,7 +274,7 @@ public class GatewaysManager { return Load.getLoadByValue(location.getAverageLoad(transportType)); } - private Pair getGatewayFromTimezoneCalculation(int nClosest, TransportType[] transportTypes, @Nullable String city) { + private GatewayOptions getGatewayFromTimezoneCalculation(int nClosest, TransportType[] transportTypes, @Nullable String city) { List list = new ArrayList<>(gateways.values()); GatewaySelector gatewaySelector = new GatewaySelector(list); Gateway gateway; @@ -277,7 +285,7 @@ public class GatewaysManager { if ((city == null && gateway.supportsTransport(transportType)) || (gateway.getName().equals(city) && gateway.supportsTransport(transportType))) { if (found == nClosest) { - return new Pair<>(gateway, transportType); + return new GatewayOptions(gateway, transportType); } found++; } @@ -287,14 +295,14 @@ public class GatewaysManager { return null; } - private Pair getGatewayFromPresortedList(int nClosest, TransportType[] transportTypes, @Nullable String city) { + private GatewayOptions getGatewayFromPresortedList(int nClosest, TransportType[] transportTypes, @Nullable String city) { int found = 0; for (Gateway gateway : presortedList) { 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); + return new GatewayOptions(gateway, transportType); } found++; } @@ -387,10 +395,9 @@ public class GatewaysManager { if (PreferenceHelper.useObfuscationPinning(context)) { try { - TransportType transportType = getObfuscationPinningKCP(context) ? OBFS4_KCP : OBFS4; Transport[] transports = new Transport[]{ - new Transport(transportType.toString(), - new String[]{"tcp"}, + new Transport(OBFS4.toString(), + new String[]{getObfuscationPinningKCP(context) ? "kcp" : "tcp"}, new String[]{getObfuscationPinningPort(context)}, getObfuscationPinningCert(context))}; GatewayJson.Capabilities capabilities = new GatewayJson.Capabilities(false, false, false, transports, false); 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 72a0d80a..141f6274 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java @@ -17,7 +17,6 @@ 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 de.blinkt.openvpn.core.connection.Connection.TransportType.PT; import static se.leap.bitmaskclient.base.models.Constants.CAPABILITIES; @@ -63,7 +62,6 @@ public class VpnConfigGenerator { private final JSONObject gateway; private final JSONObject secrets; private JSONObject obfs4Transport; - private JSONObject obfs4TKcpTransport; private final int apiVersion; private final boolean preferUDP; private final boolean experimentalTransports; @@ -122,11 +120,7 @@ public class VpnConfigGenerator { JSONObject transport = supportedTransports.getJSONObject(i); if (transport.getString(TYPE).equals(OBFS4.toString())) { obfs4Transport = transport; - if (!experimentalTransports && !obfuscationPinningKCP) { - break; - } - } else if ((experimentalTransports || obfuscationPinningKCP) && transport.getString(TYPE).equals(OBFS4_KCP.toString())) { - obfs4TKcpTransport = transport; + break; } } } @@ -154,13 +148,6 @@ public class VpnConfigGenerator { e.printStackTrace(); } } - if (supportsObfs4Kcp()) { - try { - profiles.put(OBFS4_KCP, createProfile(OBFS4_KCP)); - } catch (ConfigParser.ConfigParseError | NumberFormatException | JSONException | IOException e) { - e.printStackTrace(); - } - } if (profiles.isEmpty()) { throw new ConfigParser.ConfigParseError("No supported transports detected."); } @@ -171,11 +158,7 @@ public class VpnConfigGenerator { return !useObfuscationPinning && !gatewayConfiguration(OPENVPN).isEmpty(); } private boolean supportsObfs4(){ - return obfs4Transport != null && !(useObfuscationPinning && obfuscationPinningKCP); - } - - private boolean supportsObfs4Kcp() { - return obfs4TKcpTransport != null && !(useObfuscationPinning && !obfuscationPinningKCP); + return obfs4Transport != null || useObfuscationPinning; } private String getConfigurationString(TransportType transportType) { @@ -194,9 +177,10 @@ public class VpnConfigGenerator { ConfigParser icsOpenvpnConfigParser = new ConfigParser(); icsOpenvpnConfigParser.parseConfig(new StringReader(configuration)); if (transportType == OBFS4) { - icsOpenvpnConfigParser.setObfs4Options(getObfs4Options(obfs4Transport, false)); - } else if (transportType == OBFS4_KCP) { - icsOpenvpnConfigParser.setObfs4Options(getObfs4Options(obfs4TKcpTransport, true)); + JSONArray protocols = obfs4Transport.getJSONArray(PROTOCOLS); + // FIXME: currently only one protocol per obfs4 bridge is supported in this client + String protocol = protocols.optString(0); + icsOpenvpnConfigParser.setObfs4Options(getObfs4Options(obfs4Transport, protocol.equalsIgnoreCase("kcp"))); } VpnProfile profile = icsOpenvpnConfigParser.convertProfile(transportType); @@ -208,7 +192,6 @@ public class VpnConfigGenerator { return profile; } - // TODO: whad does private Obfs4Options getObfs4Options(JSONObject transportJson, boolean useUdp) throws JSONException { JSONObject transportOptions = transportJson.getJSONObject(OPTIONS); String iatMode = transportOptions.getString("iatMode"); @@ -367,8 +350,7 @@ public class VpnConfigGenerator { case OPENVPN: return "tcp".equals(protocol) || "udp".equals(protocol); case OBFS4: - case OBFS4_KCP: - return "tcp".equals(protocol); + return "tcp".equals(protocol) || "kcp".equals(protocol); } return false; } @@ -446,10 +428,10 @@ public class VpnConfigGenerator { String remote; if (useObfsVpn()) { if (useObfuscationPinning) { - remote = REMOTE + " " + obfuscationPinningIP + " " + obfuscationPinningPort + newLine; + remote = REMOTE + " " + obfuscationPinningIP + " " + obfuscationPinningPort + " tcp" + newLine; route = "route " + obfuscationPinningIP + " 255.255.255.255 net_gateway" + newLine; } else { - remote = REMOTE + " " + ipAddress + " " + ports.getString(0) + newLine; + remote = REMOTE + " " + ipAddress + " " + ports.getString(0) + " tcp" + newLine; } } else { remote = REMOTE + " " + DISPATCHER_IP + " " + DISPATCHER_PORT + " tcp" + newLine; diff --git a/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java b/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java index 9675c877..5d3c0826 100644 --- a/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java +++ b/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java @@ -7,7 +7,6 @@ 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; @@ -29,10 +28,10 @@ import se.leap.bitmaskclient.pluggableTransports.Obfs4Options; @PrepareForTest({UUID.class, ConfigHelper.ObfsVpnHelper.class}) public class VpnProfileTest { - 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"; + 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\":\"\",\"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\":\"\",\"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\":\"\",\"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\":true,\"mPassword\":\"\",\"mTLSAuthDirection\":\"\",\"mKeyPassword\":\"\",\"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() { @@ -106,7 +105,7 @@ public class VpnProfileTest { public void toJson_obfs4_obfsvpn_kcp() throws JSONException { when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); - VpnProfile mockVpnProfile = new VpnProfile("mockProfile", OBFS4_KCP); + VpnProfile mockVpnProfile = new VpnProfile("mockProfile", OBFS4); mockVpnProfile.mConnections[0] = new Obfs4Connection(new Obfs4Options("192.168.0.1", "1234", "CERT", "1", true)); mockVpnProfile.mLastUsed = 0; String s = mockVpnProfile.toJson(); 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 3b0d5552..1b42b5d2 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java @@ -32,7 +32,6 @@ 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; @@ -220,7 +219,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).first.getRemoteIP()); + assertEquals("37.12.247.10", gatewaysManager.select(0).gateway.getRemoteIP()); } @Test @@ -233,9 +232,9 @@ public class GatewaysManagerTest { when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); - 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()); + assertEquals("manila.bitmask.net", gatewaysManager.select(0).gateway.getHost()); + assertEquals("moscow.bitmask.net", gatewaysManager.select(1).gateway.getHost()); + assertEquals("pt.demo.bitmask.net", gatewaysManager.select(2).gateway.getHost()); } @Test @@ -248,8 +247,8 @@ public class GatewaysManagerTest { when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); - assertEquals("moscow.bitmask.net", gatewaysManager.select(0).first.getHost()); - assertEquals("pt.demo.bitmask.net", gatewaysManager.select(1).first.getHost()); + assertEquals("moscow.bitmask.net", gatewaysManager.select(0).gateway.getHost()); + assertEquals("pt.demo.bitmask.net", gatewaysManager.select(1).gateway.getHost()); assertNull(gatewaysManager.select(2)); } @@ -265,9 +264,9 @@ public class GatewaysManagerTest { when(PreferenceHelper.getPreferredCity(any(Context.class))).thenReturn("Paris"); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); - 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()); + assertEquals("mouette.riseup.net", gatewaysManager.select(0).gateway.getHost()); + assertEquals("hoatzin.riseup.net", gatewaysManager.select(1).gateway.getHost()); + assertEquals("zarapito.riseup.net", gatewaysManager.select(2).gateway.getHost()); } @Test @@ -281,9 +280,9 @@ public class GatewaysManagerTest { when(PreferenceHelper.getPreferredCity(any(Context.class))).thenReturn("Paris"); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); - 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()); + assertEquals("mouette.riseup.net", gatewaysManager.select(0).gateway.getHost()); + assertEquals("hoatzin.riseup.net", gatewaysManager.select(1).gateway.getHost()); + assertEquals("zarapito.riseup.net", gatewaysManager.select(2).gateway.getHost()); } @Test @@ -298,9 +297,9 @@ public class GatewaysManagerTest { when(PreferenceHelper.getPreferredCity(any(Context.class))).thenReturn("Paris"); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); - assertEquals("Paris", gatewaysManager.select(0).first.getName()); - assertEquals("Paris", gatewaysManager.select(1).first.getName()); - assertEquals("Paris", gatewaysManager.select(2).first.getName()); + assertEquals("Paris", gatewaysManager.select(0).gateway.getName()); + assertEquals("Paris", gatewaysManager.select(1).gateway.getName()); + assertEquals("Paris", gatewaysManager.select(2).gateway.getName()); assertEquals(null, gatewaysManager.select(3)); } @@ -314,9 +313,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").first.getHost()); - assertEquals("hoatzin.riseup.net", gatewaysManager.select(1, "Paris").first.getHost()); - assertEquals("zarapito.riseup.net", gatewaysManager.select(2, "Paris").first.getHost()); + assertEquals("mouette.riseup.net", gatewaysManager.select(0, "Paris").gateway.getHost()); + assertEquals("hoatzin.riseup.net", gatewaysManager.select(1, "Paris").gateway.getHost()); + assertEquals("zarapito.riseup.net", gatewaysManager.select(2, "Paris").gateway.getHost()); } @Test @@ -329,9 +328,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").first.getHost()); - assertEquals("hoatzin.riseup.net", gatewaysManager.select(1, "Paris").first.getHost()); - assertEquals("zarapito.riseup.net", gatewaysManager.select(2, "Paris").first.getHost()); + assertEquals("mouette.riseup.net", gatewaysManager.select(0, "Paris").gateway.getHost()); + assertEquals("hoatzin.riseup.net", gatewaysManager.select(1, "Paris").gateway.getHost()); + assertEquals("zarapito.riseup.net", gatewaysManager.select(2, "Paris").gateway.getHost()); } @Test @@ -345,9 +344,9 @@ public class GatewaysManagerTest { when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); - 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("Paris", gatewaysManager.select(0, "Paris").gateway.getName()); + assertEquals("Paris", gatewaysManager.select(1, "Paris").gateway.getName()); + assertEquals("Paris", gatewaysManager.select(2, "Paris").gateway.getName()); assertEquals(null, gatewaysManager.select(3, "Paris")); } @@ -534,7 +533,7 @@ public class GatewaysManagerTest { mockStatic(PreferenceHelper.class); when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); - List locations = gatewaysManager.getSortedGatewayLocations(OBFS4_KCP); + List locations = gatewaysManager.getSortedGatewayLocations(OBFS4); assertEquals(3, locations.size()); } @@ -548,7 +547,6 @@ public class GatewaysManagerTest { 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)); } 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 4fb178fc..6b1c5bf3 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java @@ -9,7 +9,6 @@ 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; @@ -34,6 +33,7 @@ import java.util.HashMap; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.connection.Connection; +import de.blinkt.openvpn.core.connection.Obfs4Connection; import se.leap.bitmaskclient.base.utils.ConfigHelper; import se.leap.bitmaskclient.testutils.MockHelper; import se.leap.bitmaskclient.testutils.TestSetupHelper; @@ -1688,7 +1688,7 @@ public class VpnConfigGeneratorTest { @Test public void testGenerateVpnProfileExperimentalTransportsEnabled () throws Exception { - gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_kcp_gateways.json"))).getJSONArray("gateways").getJSONObject(0); + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_kcp_gateways.json"))).getJSONArray("gateways").getJSONObject(2); generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_kcp_gateways.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; @@ -1696,29 +1696,27 @@ public class VpnConfigGeneratorTest { configuration.preferUDP = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); - assertTrue(vpnProfiles.containsKey(OBFS4)); - assertTrue(vpnProfiles.containsKey(OBFS4_KCP)); + assertTrue(vpnProfiles.containsKey(OBFS4) && ((Obfs4Connection)vpnProfiles.get(OBFS4).mConnections[0]).getDispatcherOptions().udp); assertTrue(vpnProfiles.containsKey(OPENVPN)); } @Test public void testGenerateVpnProfile_experimentalTransportsEnabled_KCPMisconfiguredWithUDP_SkippingObfsKCP () throws Exception { - gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_misconfigured_kcp_gateways.json"))).getJSONArray("gateways").getJSONObject(0); + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_misconfigured_kcp_gateways.json"))).getJSONArray("gateways").getJSONObject(2); generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_misconfigured_kcp_gateways.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); - assertTrue(vpnProfiles.containsKey(OBFS4)); - assertFalse(vpnProfiles.containsKey(OBFS4_KCP)); + assertFalse(vpnProfiles.containsKey(OBFS4)); assertTrue(vpnProfiles.containsKey(OPENVPN)); } @Test public void testGenerateVpnProfile_ObfuscationPinningEnabled_obfs4AndOpenvpnProfile () throws Exception { - gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_kcp_gateways.json"))).getJSONArray("gateways").getJSONObject(0); + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_kcp_gateways.json"))).getJSONArray("gateways").getJSONObject(1); generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_kcp_gateways.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; @@ -1728,7 +1726,8 @@ public class VpnConfigGeneratorTest { HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); assertFalse("has no openvpn profile", vpnProfiles.containsKey(OPENVPN)); assertTrue("has obfs4 profile", vpnProfiles.containsKey(OBFS4)); - assertFalse("has no obfs4 kcp profile", vpnProfiles.containsKey(OBFS4_KCP)); + assertTrue("bridge is running KCP", vpnProfiles.get(OBFS4).mGatewayIp.equals("1.2.3.4")); + } @Test @@ -1738,16 +1737,17 @@ public class VpnConfigGeneratorTest { VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; configuration.useObfuscationPinning = true; - configuration.obfuscationProxyKCP = true; + configuration.obfuscationProxyKCP = false; configuration.obfuscationProxyPort = "443"; configuration.obfuscationProxyIP = "5.6.7.8"; configuration.obfuscationProxyCert = "asdfasdf"; configuration.remoteGatewayIP = "1.2.3.4"; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); - assertTrue("has obfs4_kcp profile", vpnProfiles.containsKey(OBFS4_KCP)); assertFalse("has openvpn profile", vpnProfiles.containsKey(OPENVPN)); - assertFalse("has no obfs4 profile", vpnProfiles.containsKey(OBFS4)); + assertTrue("has obfs4 profile", vpnProfiles.containsKey(OBFS4)); + assertTrue("bridge is pinned one", vpnProfiles.get(OBFS4).getTransportType() == OBFS4 && !vpnProfiles.get(OBFS4).mConnections[0].isUseUdp() ); + assertTrue("bridge is running KCP", ((Obfs4Connection) vpnProfiles.get(OBFS4).mConnections[0]).getDispatcherOptions().udp == false); } @Test @@ -1765,9 +1765,10 @@ public class VpnConfigGeneratorTest { vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); - assertTrue("has obfs4_kcp profile", vpnProfiles.containsKey(OBFS4_KCP)); assertFalse("has openvpn profile", vpnProfiles.containsKey(OPENVPN)); - assertFalse("has no obfs4 profile", vpnProfiles.containsKey(OBFS4)); + assertTrue("has no obfs4 profile", vpnProfiles.containsKey(OBFS4)); + assertTrue("bridge is pinned one", vpnProfiles.get(OBFS4).getTransportType() == OBFS4 && vpnProfiles.get(OBFS4).mGatewayIp.equals("1.2.3.4")); + assertTrue("bridge is running KCP", ((Obfs4Connection) vpnProfiles.get(OBFS4).mConnections[0]).getDispatcherOptions().udp == true); } } \ No newline at end of file diff --git a/app/src/test/resources/ptdemo_kcp_gateways.json b/app/src/test/resources/ptdemo_kcp_gateways.json index 332c8c11..8c2c9d32 100644 --- a/app/src/test/resources/ptdemo_kcp_gateways.json +++ b/app/src/test/resources/ptdemo_kcp_gateways.json @@ -19,19 +19,6 @@ "iatMode": "0" } }, - { - "type":"obfs4-1", - "protocols":[ - "tcp" - ], - "ports":[ - "23050" - ], - "options": { - "cert": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1", - "iatMode": "0" - } - }, { "type":"openvpn", "protocols":[ @@ -101,9 +88,9 @@ ] }, { - "type":"obfs4-1", + "type":"obfs4", "protocols":[ - "tcp" + "kcp" ], "ports":[ "23050" diff --git a/app/src/test/resources/ptdemo_misconfigured_kcp_gateways.json b/app/src/test/resources/ptdemo_misconfigured_kcp_gateways.json index 94a1eafd..28a6a201 100644 --- a/app/src/test/resources/ptdemo_misconfigured_kcp_gateways.json +++ b/app/src/test/resources/ptdemo_misconfigured_kcp_gateways.json @@ -20,7 +20,7 @@ } }, { - "type":"obfs4-1", + "type":"obfs4", "protocols":[ "udp" ], @@ -101,7 +101,7 @@ ] }, { - "type":"obfs4-1", + "type":"obfs4", "protocols":[ "udp" ], diff --git a/app/src/test/resources/ptdemo_only_experimental_transports_gateways.json b/app/src/test/resources/ptdemo_only_experimental_transports_gateways.json index 31c919e7..8f98d170 100644 --- a/app/src/test/resources/ptdemo_only_experimental_transports_gateways.json +++ b/app/src/test/resources/ptdemo_only_experimental_transports_gateways.json @@ -7,9 +7,9 @@ "limited":false, "transport":[ { - "type":"obfs4-1", + "type":"obfs4", "protocols":[ - "tcp" + "kcp" ], "ports":[ "23050" @@ -75,9 +75,9 @@ ] }, { - "type":"obfs4-1", + "type":"obfs4", "protocols":[ - "tcp" + "kcp" ], "ports":[ "23050" -- cgit v1.2.3 From 939901a89abb169648423473056260335d3af639 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 6 Apr 2023 01:08:05 +0200 Subject: first pass on obfs4-hop pt integration --- .../main/java/de/blinkt/openvpn/VpnProfile.java | 17 +- .../java/de/blinkt/openvpn/core/ConfigParser.java | 20 +- .../de/blinkt/openvpn/core/OpenVPNService.java | 20 +- .../blinkt/openvpn/core/connection/Connection.java | 56 ++++- .../openvpn/core/connection/Obfs4Connection.java | 6 +- .../core/connection/Obfs4HopConnection.java | 48 ++++ .../leap/bitmaskclient/base/models/Constants.java | 9 + .../leap/bitmaskclient/base/models/Provider.java | 5 +- .../leap/bitmaskclient/base/models/Transport.java | 120 +++++++++- .../leap/bitmaskclient/eip/VpnConfigGenerator.java | 242 +++++++++++---------- .../pluggableTransports/HoppingConfig.java | 49 +++++ .../pluggableTransports/HoppingObfsVpnClient.java | 72 ++++++ .../pluggableTransports/Obfs4Options.java | 21 +- .../pluggableTransports/ObfsVpnClient.java | 25 ++- .../pluggableTransports/PtClientBuilder.java | 18 ++ .../pluggableTransports/PtClientInterface.java | 9 + .../pluggableTransports/ShapeshifterClient.java | 5 +- .../java/de/blinkt/openvpn/VpnProfileTest.java | 104 +++++++-- .../bitmaskclient/base/models/GatewayJsonTest.java | 2 +- .../bitmaskclient/base/models/TransportTest.java | 68 ++++++ .../bitmaskclient/eip/VpnConfigGeneratorTest.java | 41 +++- .../resources/ptdemo_obfs4hop_kcp_gateways.json | 178 +++++++++++++++ .../ptdemo_obfs4hop_misconfigured_udp_gateway.json | 178 +++++++++++++++ .../resources/ptdemo_obfs4hop_tcp_gateways.json | 178 +++++++++++++++ 24 files changed, 1286 insertions(+), 205 deletions(-) create mode 100644 app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4HopConnection.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingConfig.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingObfsVpnClient.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/pluggableTransports/PtClientBuilder.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/pluggableTransports/PtClientInterface.java create mode 100644 app/src/test/java/se/leap/bitmaskclient/base/models/TransportTest.java create mode 100644 app/src/test/resources/ptdemo_obfs4hop_kcp_gateways.json create mode 100644 app/src/test/resources/ptdemo_obfs4hop_misconfigured_udp_gateway.json create mode 100644 app/src/test/resources/ptdemo_obfs4hop_tcp_gateways.json (limited to 'app/src') diff --git a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java index 3428e0c4..9baac195 100644 --- a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java +++ b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java @@ -5,8 +5,6 @@ package de.blinkt.openvpn; -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.PROVIDER_PROFILE; import static se.leap.bitmaskclient.base.utils.ConfigHelper.stringEqual; @@ -191,7 +189,7 @@ public class VpnProfile implements Serializable, Cloneable { private int mProfileVersion; public boolean mBlockUnusedAddressFamilies = true; public String mGatewayIp; - private final boolean mUseObfs4; + private final int mTransportType; public VpnProfile(String name, Connection.TransportType transportType) { mUuid = UUID.randomUUID(); @@ -200,7 +198,7 @@ public class VpnProfile implements Serializable, Cloneable { mConnections = new Connection[1]; mLastUsed = System.currentTimeMillis(); - mUseObfs4 = transportType == OBFS4; + mTransportType = transportType.toInt(); } public static String openVpnEscape(String unescaped) { @@ -266,7 +264,7 @@ public class VpnProfile implements Serializable, Cloneable { if (obj instanceof VpnProfile) { VpnProfile vp = (VpnProfile) obj; return stringEqual(vp.mGatewayIp, mGatewayIp) && - vp.mUseObfs4 == mUseObfs4; + vp.mTransportType == mTransportType; } return false; } @@ -297,15 +295,12 @@ public class VpnProfile implements Serializable, Cloneable { } public boolean usePluggableTransports() { - return mUseObfs4; + Connection.TransportType type = Connection.TransportType.fromInt(mTransportType); + return type != null && type.isPluggableTransport(); } public Connection.TransportType getTransportType() { - if (mUseObfs4) { - return OBFS4; - } else { - return OPENVPN; - } + return Connection.TransportType.fromInt(mTransportType); } public String getName() { diff --git a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java index 6063ea53..e8d333e3 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java +++ b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java @@ -27,6 +27,7 @@ import java.util.Vector; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.connection.Connection; import de.blinkt.openvpn.core.connection.Obfs4Connection; +import de.blinkt.openvpn.core.connection.Obfs4HopConnection; import de.blinkt.openvpn.core.connection.OpenvpnConnection; import se.leap.bitmaskclient.pluggableTransports.Obfs4Options; @@ -807,8 +808,23 @@ public class ConfigParser { e.printStackTrace(); return null; } - else - conn = transportType.getMetaType() == PT ? new Obfs4Connection(obfs4Options) : new OpenvpnConnection(); + else { + switch (transportType) { + case OBFS4: + conn = new Obfs4Connection(obfs4Options); + break; + case OBFS4_HOP: + conn = new Obfs4HopConnection(obfs4Options); + break; + case OPENVPN: + conn = new OpenvpnConnection(); + break; + default: + throw new ConfigParseError("Unexpected transport type: " + transportType); + + } + + } Vector port = getOption("port", 1, 1); if (port != null) { 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 0ae7639e..6adffda1 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -12,7 +12,6 @@ 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; import android.content.Intent; import android.content.IntentFilter; @@ -41,7 +40,6 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collection; -import java.util.Locale; import java.util.Vector; import de.blinkt.openvpn.VpnProfile; @@ -53,7 +51,8 @@ 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.PtClientBuilder; +import se.leap.bitmaskclient.pluggableTransports.PtClientInterface; import se.leap.bitmaskclient.pluggableTransports.ShapeshifterClient; @@ -91,7 +90,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac private Runnable mOpenVPNThread; private VpnNotificationManager notificationManager; private ShapeshifterClient shapeshifter; - private ObfsVpnClient obfsVpnClient; + private PtClientInterface obfsVpnClient; private FirewallManager firewallManager; private final IBinder mBinder = new IOpenVPNServiceInternal.Stub() { @@ -415,18 +414,19 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac stopOldOpenVPNProcess(); // An old running VPN should now be exited mStarting = false; - - if (mProfile.usePluggableTransports() && connection instanceof Obfs4Connection) { - Obfs4Connection obfs4Connection = (Obfs4Connection) connection; + Connection.TransportType transportType = connection.getTransportType(); + if (mProfile.usePluggableTransports() && transportType.isPluggableTransport()) { if (useObfsVpn()) { if (obfsVpnClient != null && obfsVpnClient.isStarted()) { obfsVpnClient.stop(); } - obfsVpnClient = new ObfsVpnClient(obfs4Connection.getDispatcherOptions()); + obfsVpnClient = PtClientBuilder.getPtClient(connection); int runningSocksPort = obfsVpnClient.start(); - connection.setProxyPort(String.valueOf(runningSocksPort)); + if (connection.getTransportType() == Connection.TransportType.OBFS4) { + connection.setProxyPort(String.valueOf(runningSocksPort)); + } } else if (shapeshifter == null) { - shapeshifter = new ShapeshifterClient(obfs4Connection.getDispatcherOptions()); + shapeshifter = new ShapeshifterClient(((Obfs4Connection) connection).getObfs4Options()); shapeshifter.start(); } } 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 137b3f16..6dc78a30 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 @@ -5,6 +5,8 @@ package de.blinkt.openvpn.core.connection; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.*; + import android.text.TextUtils; import com.google.gson.annotations.JsonAdapter; @@ -42,7 +44,7 @@ public abstract class Connection implements Serializable, Cloneable { TCP("tcp"), KCP("kcp"); - String protocol; + final String protocol; TransportProtocol(String transportProtocol) { this.protocol = transportProtocol; @@ -55,11 +57,12 @@ public abstract class Connection implements Serializable, Cloneable { } public enum TransportType { OBFS4("obfs4"), + OBFS4_HOP("obfs4Hop"), OPENVPN("openvpn"), PT("metaTransport"); - String transport; + final String transport; TransportType(String transportType) { this.transport = transportType; @@ -70,12 +73,57 @@ public abstract class Connection implements Serializable, Cloneable { return transport; } + public int toInt() { + switch (this) { + case PT: + return 0; + case OPENVPN: + return 1; + case OBFS4: + return 2; + case OBFS4_HOP: + return 3; + default: + return -1; + } + } + + public static TransportType fromString(String value) { + switch (value) { + case "obfs4": + return OBFS4; + case "obfs4-hop": + return OBFS4_HOP; + case "metaTransport": + return PT; + case "openvpn": + return OPENVPN; + default: + throw new IllegalArgumentException(value + " is not a valid value for TransportType."); + } + } + + public static TransportType fromInt(int value) { + switch (value) { + case 0: + return PT; + case 1: + return OPENVPN; + case 2: + return OBFS4; + case 3: + return OBFS4_HOP; + default: + return null; + } + } + public boolean isPluggableTransport() { - return this == OBFS4 || this == PT; + return this == OBFS4 || this == OBFS4_HOP || this == PT; } public TransportType getMetaType() { - if (this == OBFS4 || this == PT) { + if (this == OBFS4 || this == OBFS4_HOP || 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 c77c23fd..73bfccdc 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 @@ -19,8 +19,8 @@ public class Obfs4Connection extends Connection { public Obfs4Connection(Obfs4Options options) { if (useObfsVpn()) { - setServerName(options.remoteIP); - setServerPort(options.remotePort); + setServerName(options.gatewayIP); + setServerPort(options.transport.getPorts()[0]); setProxyName(ObfsVpnClient.SOCKS_IP); setProxyPort(String.valueOf(ObfsVpnClient.SOCKS_PORT.get())); setProxyType(ProxyType.SOCKS5); @@ -53,7 +53,7 @@ public class Obfs4Connection extends Connection { } - public Obfs4Options getDispatcherOptions() { + public Obfs4Options getObfs4Options() { return options; } diff --git a/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4HopConnection.java b/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4HopConnection.java new file mode 100644 index 00000000..f983ae20 --- /dev/null +++ b/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4HopConnection.java @@ -0,0 +1,48 @@ +package de.blinkt.openvpn.core.connection; + +import se.leap.bitmaskclient.pluggableTransports.HoppingObfsVpnClient; +import se.leap.bitmaskclient.pluggableTransports.Obfs4Options; + + +/** + * Created by cyberta on 08.03.19. + */ + +public class Obfs4HopConnection extends Connection { + + private static final String TAG = Obfs4HopConnection.class.getName(); + private Obfs4Options options; + + public Obfs4HopConnection(Obfs4Options options) { + setServerName(HoppingObfsVpnClient.IP); + setServerPort(String.valueOf(HoppingObfsVpnClient.PORT)); + setProxyName(""); + setProxyPort(""); + setProxyType(ProxyType.NONE); + + + setUseUdp(true); + setProxyAuthUser(null); + setProxyAuthPassword(null); + setUseProxyAuth(false); + this.options = options; + } + + @Override + public Connection clone() throws CloneNotSupportedException { + Obfs4HopConnection connection = (Obfs4HopConnection) super.clone(); + connection.options = this.options; + return connection; + } + + @Override + public TransportType getTransportType() { + return TransportType.OBFS4_HOP; + } + + + public Obfs4Options getObfs4Options() { + return options; + } + +} 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 ee5bd2a7..57467974 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 @@ -179,10 +179,19 @@ public interface Constants { String PORTS = "ports"; String PROTOCOLS = "protocols"; String UDP = "udp"; + String TCP = "tcp"; + String KCP = "kcp"; String CAPABILITIES = "capabilities"; String TRANSPORT = "transport"; String TYPE = "type"; String OPTIONS = "options"; + String IAT_MODE = "iatMode"; + String CERT = "cert"; + String CERTS = "certs"; + String ENDPOINTS = "endpoints"; + String PORT_SEED = "port_seed"; + String PORT_COUNT = "port_count"; + String EXPERIMENTAL = "experimental"; String VERSION = "version"; String NAME = "name"; String TIMEZONE = "timezone"; 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 62fb1fd2..57653263 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 @@ -19,6 +19,7 @@ package se.leap.bitmaskclient.base.models; import static de.blinkt.openvpn.core.connection.Connection.TransportProtocol.KCP; import static de.blinkt.openvpn.core.connection.Connection.TransportProtocol.TCP; import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_HOP; 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; @@ -184,13 +185,13 @@ public final class Provider implements Parcelable { public boolean supportsPluggableTransports() { if (useObfsVpn()) { - return supportsTransports(new Pair[]{new Pair<>(OBFS4, TCP), new Pair<>(OBFS4, KCP)}); + return supportsTransports(new Pair[]{new Pair<>(OBFS4, TCP), new Pair<>(OBFS4, KCP), new Pair<>(OBFS4_HOP, TCP), new Pair<>(OBFS4_HOP, KCP)}); } return supportsTransports(new Pair[]{new Pair<>(OBFS4, TCP)}); } public boolean supportsExperimentalPluggableTransports() { - return supportsTransports(new Pair[]{new Pair<>(OBFS4, KCP)}); + return supportsTransports(new Pair[]{new Pair<>(OBFS4, KCP), new Pair<>(OBFS4_HOP, TCP), new Pair<>(OBFS4_HOP, KCP)}); } private boolean supportsTransports(Pair[] transportTypes) { diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Transport.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Transport.java index 90a033dd..7d9b61a7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/models/Transport.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Transport.java @@ -1,21 +1,57 @@ package se.leap.bitmaskclient.base.models; +import androidx.annotation.Nullable; + +import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.annotations.SerializedName; import org.json.JSONObject; -public class Transport { +import java.io.Serializable; + +import de.blinkt.openvpn.core.connection.Connection; + +public class Transport implements Serializable { private String type; private String[] protocols; + @Nullable private String[] ports; + @Nullable private Options options; public Transport(String type, String[] protocols, String[] ports, String cert) { + this(type, protocols, ports, new Options(cert, "0")); + } + + public Transport(String type, String[] protocols, String[] ports, Options options) { this.type = type; this.protocols = protocols; this.ports = ports; - this.options = new Options(cert); + this.options = options; + } + + public String getType() { + return type; + } + + public Connection.TransportType getTransportType() { + return Connection.TransportType.fromString(type); + } + + public String[] getProtocols() { + return protocols; + } + + @Nullable + public String[] getPorts() { + return ports; + } + + @Nullable + public Options getOptions() { + return options; } @Override @@ -25,16 +61,65 @@ public class Transport { public static Transport fromJson(JSONObject json) { GsonBuilder builder = new GsonBuilder(); - return builder.create().fromJson(json.toString(), Transport.class); + return builder. + setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES). + create(). + fromJson(json.toString(), Transport.class); } - public static class Options { + public static class Options implements Serializable { + @Nullable private String cert; + @SerializedName("iatMode") private String iatMode; - public Options(String cert) { + @Nullable + private Endpoint[] endpoints; + + private boolean experimental; + + private int portSeed; + + private int portCount; + + + public Options(String cert, String iatMode) { this.cert = cert; - this.iatMode = "0"; + this.iatMode = iatMode; + } + + public Options(String iatMode, Endpoint[] endpoints, int portSeed, int portCount, boolean experimental) { + this.iatMode = iatMode; + this.endpoints = endpoints; + this.portSeed = portSeed; + this.portCount = portCount; + this.experimental = experimental; + } + + @Nullable + public String getCert() { + return cert; + } + + public String getIatMode() { + return iatMode; + } + + @Nullable + public Endpoint[] getEndpoints() { + return endpoints; + } + + public boolean isExperimental() { + return experimental; + } + + public int getPortSeed() { + return portSeed; + } + + public int getPortCount() { + return portCount; } @Override @@ -44,6 +129,29 @@ public class Transport { } + public static class Endpoint implements Serializable { + private String ip; + private String cert; + + public Endpoint(String ip, String cert) { + this.ip = ip; + this.cert = cert; + } + + @Override + public String toString() { + return new Gson().toJson(this); + } + + public String getIp() { + return ip; + } + + public String getCert() { + return cert; + } + } + } 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 141f6274..7229f7ff 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java @@ -17,19 +17,19 @@ package se.leap.bitmaskclient.eip; import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_HOP; import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN; -import static de.blinkt.openvpn.core.connection.Connection.TransportType.PT; import static se.leap.bitmaskclient.base.models.Constants.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.KCP; 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.TCP; 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.ShapeshifterClient.DISPATCHER_IP; @@ -54,14 +54,16 @@ import de.blinkt.openvpn.core.VpnStatus; import de.blinkt.openvpn.core.connection.Connection; import de.blinkt.openvpn.core.connection.Connection.TransportType; import se.leap.bitmaskclient.base.models.Provider; +import se.leap.bitmaskclient.base.models.Transport; import se.leap.bitmaskclient.base.utils.ConfigHelper; +import se.leap.bitmaskclient.pluggableTransports.HoppingObfsVpnClient; import se.leap.bitmaskclient.pluggableTransports.Obfs4Options; public class VpnConfigGenerator { private final JSONObject generalConfiguration; private final JSONObject gateway; private final JSONObject secrets; - private JSONObject obfs4Transport; + HashMap transports = new HashMap<>(); private final int apiVersion; private final boolean preferUDP; private final boolean experimentalTransports; @@ -113,19 +115,14 @@ public class VpnConfigGenerator { public void checkCapabilities() throws ConfigParser.ConfigParseError { try { - if (apiVersion >= 3) { JSONArray supportedTransports = gateway.getJSONObject(CAPABILITIES).getJSONArray(TRANSPORT); for (int i = 0; i < supportedTransports.length(); i++) { - JSONObject transport = supportedTransports.getJSONObject(i); - if (transport.getString(TYPE).equals(OBFS4.toString())) { - obfs4Transport = transport; - break; - } + Transport transport = Transport.fromJson(supportedTransports.getJSONObject(i)); + transports.put(transport.getTransportType(), transport); } } - - } catch (JSONException e) { + } catch (Exception e) { throw new ConfigParser.ConfigParseError("Api version ("+ apiVersion +") did not match required JSON fields"); } } @@ -141,11 +138,15 @@ public class VpnConfigGenerator { e.printStackTrace(); } } - if (supportsObfs4()) { - try { - profiles.put(OBFS4, createProfile(OBFS4)); - } catch (ConfigParser.ConfigParseError | NumberFormatException | JSONException | IOException e) { - e.printStackTrace(); + if (apiVersion >= 3) { + for (TransportType transportType : transports.keySet()) { + if (transportType.isPluggableTransport()) { + try { + profiles.put(transportType, createProfile(transportType)); + } catch (ConfigParser.ConfigParseError | NumberFormatException | JSONException | IOException e) { + e.printStackTrace(); + } + } } } if (profiles.isEmpty()) { @@ -155,10 +156,9 @@ public class VpnConfigGenerator { } private boolean supportsOpenvpn() { - return !useObfuscationPinning && !gatewayConfiguration(OPENVPN).isEmpty(); - } - private boolean supportsObfs4(){ - return obfs4Transport != null || useObfuscationPinning; + return !useObfuscationPinning && + ((apiVersion >= 3 && transports.containsKey(OPENVPN)) || + (apiVersion < 3 && !gatewayConfiguration(OPENVPN).isEmpty())); } private String getConfigurationString(TransportType transportType) { @@ -176,11 +176,8 @@ public class VpnConfigGenerator { String configuration = getConfigurationString(transportType); ConfigParser icsOpenvpnConfigParser = new ConfigParser(); icsOpenvpnConfigParser.parseConfig(new StringReader(configuration)); - if (transportType == OBFS4) { - JSONArray protocols = obfs4Transport.getJSONArray(PROTOCOLS); - // FIXME: currently only one protocol per obfs4 bridge is supported in this client - String protocol = protocols.optString(0); - icsOpenvpnConfigParser.setObfs4Options(getObfs4Options(obfs4Transport, protocol.equalsIgnoreCase("kcp"))); + if (transportType == OBFS4 || transportType == OBFS4_HOP) { + icsOpenvpnConfigParser.setObfs4Options(getObfs4Options(transportType)); } VpnProfile profile = icsOpenvpnConfigParser.convertProfile(transportType); @@ -192,21 +189,19 @@ public class VpnConfigGenerator { return profile; } - 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 = transportJson.getJSONArray(PORTS).getString(0); + private Obfs4Options getObfs4Options(TransportType transportType) throws JSONException { String ip = gateway.getString(IP_ADDRESS); - boolean udp = useUdp; - + Transport transport; if (useObfuscationPinning) { - cert = obfuscationPinningCert; - port = obfuscationPinningPort; + transport = new Transport(OBFS4.toString(), + new String[]{obfuscationPinningKCP ? KCP : TCP}, + new String[]{obfuscationPinningPort}, + obfuscationPinningCert); ip = obfuscationPinningIP; - udp = obfuscationPinningKCP; + } else { + transport = transports.get(transportType); } - return new Obfs4Options(ip, port, cert, iatMode, udp); + return new Obfs4Options(ip, transport); } private String generalConfiguration() { @@ -254,8 +249,7 @@ public class VpnConfigGenerator { new String[]{ipAddress} : new String[]{ipAddress6, ipAddress}; - JSONArray transports = capabilities.getJSONArray(TRANSPORT); - gatewayConfigMinApiv3(transportType, stringBuilder, ipAddresses, transports); + gatewayConfigMinApiv3(transportType, stringBuilder, ipAddresses); break; } } catch (JSONException e) { @@ -271,11 +265,11 @@ public class VpnConfigGenerator { return remotes; } - private void gatewayConfigMinApiv3(TransportType transportType, StringBuilder stringBuilder, String[] ipAddresses, JSONArray transports) throws JSONException { - if (transportType.getMetaType() == PT) { - ptGatewayConfigMinApiv3(stringBuilder, ipAddresses, transportType, transports); + private void gatewayConfigMinApiv3(TransportType transportType, StringBuilder stringBuilder, String[] ipAddresses) throws JSONException { + if (transportType.isPluggableTransport()) { + ptGatewayConfigMinApiv3(stringBuilder, ipAddresses, transports.get(transportType)); } else { - ovpnGatewayConfigMinApi3(stringBuilder, ipAddresses, transports); + ovpnGatewayConfigMinApi3(stringBuilder, ipAddresses, transports.get(OPENVPN)); } } @@ -294,19 +288,16 @@ public class VpnConfigGenerator { } } - private void ovpnGatewayConfigMinApi3(StringBuilder stringBuilder, String[] ipAddresses, JSONArray transports) throws JSONException { - String port; - String protocol; - JSONObject openvpnTransport = getTransport(transports, OPENVPN); - JSONArray ports = openvpnTransport.getJSONArray(PORTS); - JSONArray protocols = openvpnTransport.getJSONArray(PROTOCOLS); + private void ovpnGatewayConfigMinApi3(StringBuilder stringBuilder, String[] ipAddresses, Transport transport) { + if (transport.getProtocols() == null || transport.getPorts() == null) { + VpnStatus.logError("Misconfigured provider: missing details for transport openvpn on gateway " + ipAddresses[0]); + return; + } if (preferUDP) { StringBuilder udpRemotes = new StringBuilder(); StringBuilder tcpRemotes = new StringBuilder(); - for (int i = 0; i < protocols.length(); i++) { - protocol = protocols.optString(i); - for (int j = 0; j < ports.length(); j++) { - port = ports.optString(j); + for (String protocol : transport.getProtocols()) { + for (String port : transport.getPorts()) { for (String ipAddress : ipAddresses) { String newRemote = REMOTE + " " + ipAddress + " " + port + " " + protocol + newLine; if (UDP.equals(protocol)) { @@ -320,10 +311,8 @@ public class VpnConfigGenerator { stringBuilder.append(udpRemotes.toString()); stringBuilder.append(tcpRemotes.toString()); } else { - for (int j = 0; j < ports.length(); j++) { - port = ports.getString(j); - for (int k = 0; k < protocols.length(); k++) { - protocol = protocols.optString(k); + for (String protocol : transport.getProtocols()) { + for (String port : transport.getPorts()) { for (String ipAddress : ipAddresses) { String newRemote = REMOTE + " " + ipAddress + " " + port + " " + protocol + newLine; stringBuilder.append(newRemote); @@ -333,31 +322,18 @@ public class VpnConfigGenerator { } } - private JSONObject getTransport(JSONArray transports, TransportType transportType) throws JSONException { - JSONObject selectedTransport = new JSONObject(); - for (int i = 0; i < transports.length(); i++) { - JSONObject transport = transports.getJSONObject(i); - if (transport.getString(TYPE).equals(transportType.toString())) { - selectedTransport = transport; - break; - } - } - return selectedTransport; - } - private boolean isAllowedProtocol(TransportType transportType, String protocol) { switch (transportType) { case OPENVPN: - return "tcp".equals(protocol) || "udp".equals(protocol); + return TCP.equals(protocol) || UDP.equals(protocol); + case OBFS4_HOP: case OBFS4: - return "tcp".equals(protocol) || "kcp".equals(protocol); + return TCP.equals(protocol) || KCP.equals(protocol); } return false; } - private void ptGatewayConfigMinApiv3(StringBuilder stringBuilder, String[] ipAddresses, TransportType transportType, JSONArray transports) throws JSONException { - JSONObject ptTransport = getTransport(transports, transportType); - JSONArray ptProtocols = ptTransport.getJSONArray(PROTOCOLS); + private void ptGatewayConfigMinApiv3(StringBuilder stringBuilder, String[] ipAddresses, Transport transport) { //for now only use ipv4 gateway the syntax route remote_host 255.255.255.255 net_gateway is not yet working // https://community.openvpn.net/openvpn/ticket/1161 @@ -381,63 +357,97 @@ public class VpnConfigGenerator { } if (ipAddress == null) { - VpnStatus.logError("No matching IPv4 address found to configure obfs4."); + VpnStatus.logError("Misconfigured provider: No matching IPv4 address found to configure obfs4."); return; } - if (!useObfuscationPinning) { - // check if at least one openvpn protocol is TCP, openvpn in UDP is currently not supported for obfs4, - // however on the wire UDP might be used - boolean hasOpenvpnTcp = false; - JSONObject openvpnTransport = getTransport(transports, OPENVPN); - JSONArray gatewayProtocols = openvpnTransport.getJSONArray(PROTOCOLS); - for (int i = 0; i < gatewayProtocols.length(); i++) { - String protocol = gatewayProtocols.getString(i); - if (protocol.contains("tcp")) { - hasOpenvpnTcp = true; - break; - } - } - if (!hasOpenvpnTcp) { - VpnStatus.logError("obfs4 currently only allows openvpn in TCP mode! Skipping obfs4 config for ip " + ipAddress); - return; - } - } - - boolean hasAllowedPTProtocol = false; - for (int i = 0; i < ptProtocols.length(); i++) { - String protocol = ptProtocols.getString(i); - if (isAllowedProtocol(transportType, protocol)) { - hasAllowedPTProtocol = true; - break; - } + if (!openvpnModeSupportsPt(transport, ipAddress) || !hasPTAllowedProtocol(transport, ipAddress)) { + return; } - if (!hasAllowedPTProtocol) { - VpnStatus.logError("Misconfigured provider: wrong protocol defined in " + transportType.toString()+ " transport JSON."); + TransportType transportType = transport.getTransportType(); + if (transportType == OBFS4 && transport.getPorts() == null) { + VpnStatus.logError("Misconfigured provider: no ports defined in " + transport.getType() + " transport JSON for gateway " + ipAddress); return; } - JSONArray ports = ptTransport.getJSONArray(PORTS); - if (ports.isNull(0)){ - VpnStatus.logError("Misconfigured provider: no ports defined in " + transportType.toString()+ " transport JSON."); + if (transportType == OBFS4_HOP && + (transport.getOptions() == null || transport.getOptions().getEndpoints() == null || transport.getOptions().getPortCount() == 0)) { + VpnStatus.logError("Misconfigured provider: missing properties for transport " + transport.getType() + " on gateway " + ipAddress); return; } - String route = "route " + ipAddress + " 255.255.255.255 net_gateway" + newLine; - String remote; + stringBuilder.append(getRouteString(ipAddress, transport)); + stringBuilder.append(getRemoteString(ipAddress, transport)); + } + + public String getRemoteString(String ipAddress, Transport transport) { if (useObfsVpn()) { if (useObfuscationPinning) { - remote = REMOTE + " " + obfuscationPinningIP + " " + obfuscationPinningPort + " tcp" + newLine; - route = "route " + obfuscationPinningIP + " 255.255.255.255 net_gateway" + newLine; - } else { - remote = REMOTE + " " + ipAddress + " " + ports.getString(0) + " tcp" + newLine; + return REMOTE + " " + obfuscationPinningIP + " " + obfuscationPinningPort + " tcp" + newLine; } - } else { - remote = REMOTE + " " + DISPATCHER_IP + " " + DISPATCHER_PORT + " tcp" + newLine; + switch (transport.getTransportType()) { + case OBFS4: + return REMOTE + " " + ipAddress + " " + transport.getPorts()[0] + " tcp" + newLine; + case OBFS4_HOP: + return REMOTE + " " + HoppingObfsVpnClient.IP + " " + HoppingObfsVpnClient.PORT + " udp" + newLine; + default: + VpnStatus.logError("Unexpected pluggable transport type " + transport.getType() + " for gateway " + ipAddress); + return ""; + } + } + return REMOTE + " " + DISPATCHER_IP + " " + DISPATCHER_PORT + " tcp" + newLine; + } + + public String getRouteString(String ipAddress, Transport transport) { + if (useObfuscationPinning) { + return "route " + obfuscationPinningIP + " 255.255.255.255 net_gateway" + newLine; + } + if (transport.getTransportType() == OBFS4) { + return "route " + ipAddress + " 255.255.255.255 net_gateway" + newLine; + } + return newLine; + } + + // While openvpn in TCP mode is required for obfs4, openvpn in UDP mode is required for obfs4-hop + private boolean openvpnModeSupportsPt(Transport transport, String ipAddress) { + if (useObfuscationPinning) { + // we don't know if the manually pinned bridge points to a openvpn gateway with the right + // configuration, so we assume yes + return true; } - stringBuilder.append(route); - stringBuilder.append(remote); + Transport openvpnTransport = transports.get(OPENVPN); + if (openvpnTransport == null) { + return false; + } + + String[] protocols = openvpnTransport.getProtocols(); + if (protocols == null) { + VpnStatus.logError("Misconfigured provider: Protocol array is missing for openvpn gateway " + ipAddress); + return false; + } + + String requiredProtocol = transport.getTransportType() == OBFS4_HOP ? UDP : TCP; + for (String protocol : protocols) { + if (protocol.equals(requiredProtocol)) { + return true; + } + } + + VpnStatus.logError("Misconfigured provider: " + transport.getTransportType().toString() + " currently only allows openvpn in " + requiredProtocol + " mode! Skipping config for ip " + ipAddress); + return false; + } + + private boolean hasPTAllowedProtocol(Transport transport, String ipAddress) { + String[] ptProtocols = transport.getProtocols(); + for (String protocol : ptProtocols) { + if (isAllowedProtocol(transport.getTransportType(), protocol)) { + return true; + } + } + + VpnStatus.logError("Misconfigured provider: wrong protocol defined in " + transport.getType() + " transport JSON for gateway " + ipAddress); + return false; } private String secretsConfiguration() { diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingConfig.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingConfig.java new file mode 100644 index 00000000..e885166a --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingConfig.java @@ -0,0 +1,49 @@ +package se.leap.bitmaskclient.pluggableTransports; + +import androidx.annotation.NonNull; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import se.leap.bitmaskclient.base.models.Transport; + +public class HoppingConfig { + final boolean kcp; + final String proxyAddr; + final String[] remotes; + final String[] certs; + final int portSeed; + final int portCount; + final int minHopSeconds; + final int hopJitter; + + public HoppingConfig(boolean kcp, + String proxyAddr, + Transport transport, + int minHopSeconds, + int hopJitter) { + this.kcp = kcp; + this.proxyAddr = proxyAddr; + Transport.Endpoint[] endpoints = transport.getOptions().getEndpoints(); + this.remotes = new String[endpoints.length]; + this.certs = new String[endpoints.length]; + for (int i = 0; i < remotes.length; i++) { + remotes[i] = endpoints[i].getIp(); + certs[i] = endpoints[i].getCert(); + } + this.portSeed = transport.getOptions().getPortSeed(); + this.portCount = transport.getOptions().getPortCount(); + this.minHopSeconds = minHopSeconds; + this.hopJitter = hopJitter; + } + + @NonNull + @Override + public String toString() { + Gson gson = new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .create(); + return gson.toJson(this); + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingObfsVpnClient.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingObfsVpnClient.java new file mode 100644 index 00000000..1b19213f --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingObfsVpnClient.java @@ -0,0 +1,72 @@ +package se.leap.bitmaskclient.pluggableTransports; + +import static de.blinkt.openvpn.core.connection.Connection.TransportProtocol.KCP; + +import client.Client; +import client.HopClient; +import de.blinkt.openvpn.core.VpnStatus; +import se.leap.bitmaskclient.base.models.Constants; + +public class HoppingObfsVpnClient implements PtClientInterface { + + public static final int PORT = 8080; + public static final String IP = "127.0.0.1"; + + public final HopClient client; + + public HoppingObfsVpnClient(Obfs4Options options) throws IllegalStateException { + + //FIXME: use a different strategy here + //Basically we would want to track if the more performant transport protocol (KCP?/TCP?) usage was successful + //if so, we stick to it, otherwise we flip the flag + boolean kcp = Constants.KCP.equals(options.transport.getProtocols()[0]); + + if (options.transport.getOptions().getEndpoints() == null) { + throw new IllegalStateException("No Endpoints for hopping pt detected!"); + } + + HoppingConfig hoppingConfig = new HoppingConfig(kcp,IP+":"+PORT, options.transport, 10, 10); + try { + client = Client.newFFIHopClient(hoppingConfig.toString()); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + @Override + public int start() { + try { + client.setEventLogger(this); + return client.start() ? PORT : 0; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + @Override + public void stop() { + try { + client.stop(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + client.setEventLogger(null); + } + } + + @Override + public boolean isStarted() { + return client.isStarted(); + } + + @Override + public void error(String s) { + VpnStatus.logError("[hopping-obfs4] " + s); + } + + @Override + public void log(String state, String message) { + VpnStatus.logDebug("[hopping-obfs4] " + state + ": " + message); + } +} 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 b96f88ca..0dd81eb8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Obfs4Options.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Obfs4Options.java @@ -2,20 +2,15 @@ package se.leap.bitmaskclient.pluggableTransports; import java.io.Serializable; +import se.leap.bitmaskclient.base.models.Transport; + public class Obfs4Options implements Serializable { - public String cert; - 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 String gatewayIP; + public Transport transport; - 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; + public Obfs4Options(String gatewayIP, + Transport transport) { + this.gatewayIP = gatewayIP; + this.transport = transport; } - } diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java index f6c8837e..9d5ddcf9 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java @@ -1,5 +1,7 @@ package se.leap.bitmaskclient.pluggableTransports; +import static se.leap.bitmaskclient.base.models.Constants.KCP; + import android.util.Log; import java.util.Observable; @@ -7,12 +9,12 @@ import java.util.Observer; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import client.Client_; +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 class ObfsVpnClient implements Observer, PtClientInterface { public static final AtomicInteger SOCKS_PORT = new AtomicInteger(4430); public static final String SOCKS_IP = "127.0.0.1"; @@ -27,9 +29,17 @@ public class ObfsVpnClient implements Observer, client.EventLogger { 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); + public ObfsVpnClient(Obfs4Options options) throws IllegalStateException{ + //FIXME: use a different strategy here + //Basically we would want to track if the more performant transport protocol (KCP?/TCP?) usage was successful + //if so, we stick to it, otherwise we flip the flag + boolean kcp = KCP.equals(options.transport.getProtocols()[0]); + + if (options.transport.getOptions().getCert() == null) { + throw new IllegalStateException("No cert found to establish a obfs4 connection"); + } + + obfsVpnClient = Client.newClient(kcp, SOCKS_IP+":"+SOCKS_PORT.get(), options.transport.getOptions().getCert()); } /** @@ -38,6 +48,7 @@ public class ObfsVpnClient implements Observer, client.EventLogger { */ public int start() { synchronized (LOCK) { + obfsVpnClient.setEventLogger(this); Log.d(TAG, "aquired LOCK"); new Thread(this::startSync).start(); waitUntilStarted(); @@ -46,6 +57,7 @@ public class ObfsVpnClient implements Observer, client.EventLogger { return SOCKS_PORT.get(); } + // We're waiting here until the obfsvpn client has found a unbound port and started private void waitUntilStarted() { int count = -1; try { @@ -88,6 +100,8 @@ public class ObfsVpnClient implements Observer, client.EventLogger { } catch (Exception e) { e.printStackTrace(); VpnStatus.logError("[obfsvpn] " + e.getLocalizedMessage()); + } finally { + obfsVpnClient.setEventLogger(null); } pendingNetworkErrorHandling.set(false); Log.d(TAG, "stopping obfsVpnClient releasing LOCK ..."); @@ -98,6 +112,7 @@ public class ObfsVpnClient implements Observer, client.EventLogger { return obfsVpnClient.isStarted(); } + // TODO: register observer! @Override public void update(Observable observable, Object arg) { if (observable instanceof EipStatus) { diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/PtClientBuilder.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/PtClientBuilder.java new file mode 100644 index 00000000..945e3d7a --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/PtClientBuilder.java @@ -0,0 +1,18 @@ +package se.leap.bitmaskclient.pluggableTransports; + +import de.blinkt.openvpn.core.connection.Connection; +import de.blinkt.openvpn.core.connection.Obfs4Connection; +import de.blinkt.openvpn.core.connection.Obfs4HopConnection; + +public class PtClientBuilder { + public static PtClientInterface getPtClient(Connection connection) throws IllegalStateException { + switch (connection.getTransportType()) { + case OBFS4: + return new ObfsVpnClient(((Obfs4Connection) connection).getObfs4Options()); + case OBFS4_HOP: + return new HoppingObfsVpnClient(((Obfs4HopConnection) connection).getObfs4Options()); + default: + throw new IllegalStateException("Unexpected pluggable transport " + connection.getTransportType()); + } + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/PtClientInterface.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/PtClientInterface.java new file mode 100644 index 00000000..28d19a97 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/PtClientInterface.java @@ -0,0 +1,9 @@ +package se.leap.bitmaskclient.pluggableTransports; + +import client.EventLogger; + +public interface PtClientInterface extends EventLogger { + int start(); + void stop(); + boolean isStarted(); +} diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ShapeshifterClient.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ShapeshifterClient.java index f1eb0f1b..102dcf35 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ShapeshifterClient.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ShapeshifterClient.java @@ -42,6 +42,7 @@ public class ShapeshifterClient implements Observer { private int retry = 0; private final Handler reconnectHandler; + @Deprecated public class ShapeshifterLogger implements shapeshifter.Logger { @Override public void log(String s) { @@ -71,8 +72,8 @@ public class ShapeshifterClient implements Observer { private void setup(Obfs4Options options) { shapeShifter.setSocksAddr(DISPATCHER_IP+":"+DISPATCHER_PORT); - shapeShifter.setTarget(options.remoteIP+":"+options.remotePort); - shapeShifter.setCert(options.cert); + shapeShifter.setTarget(options.gatewayIP +":"+options.transport.getPorts()[0]); + shapeShifter.setCert(options.transport.getOptions().getCert()); } public void setOptions(Obfs4Options options) { diff --git a/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java b/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java index 5d3c0826..e5f9a9ca 100644 --- a/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java +++ b/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java @@ -3,11 +3,12 @@ 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_HOP; import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN; +import static se.leap.bitmaskclient.base.models.Constants.UDP; import org.json.JSONException; import org.json.JSONObject; @@ -17,10 +18,12 @@ import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; +import java.util.Arrays; import java.util.UUID; import de.blinkt.openvpn.core.connection.Obfs4Connection; import de.blinkt.openvpn.core.connection.OpenvpnConnection; +import se.leap.bitmaskclient.base.models.Transport; import se.leap.bitmaskclient.base.utils.ConfigHelper; import se.leap.bitmaskclient.pluggableTransports.Obfs4Options; @@ -28,10 +31,14 @@ import se.leap.bitmaskclient.pluggableTransports.Obfs4Options; @PrepareForTest({UUID.class, ConfigHelper.ObfsVpnHelper.class}) public class VpnProfileTest { - 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\":\"\",\"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\":\"\",\"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\":\"\",\"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\":true,\"mPassword\":\"\",\"mTLSAuthDirection\":\"\",\"mKeyPassword\":\"\",\"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"; + 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,\"mPassword\":\"\",\"mTLSAuthDirection\":\"\",\"mKeyPassword\":\"\",\"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,\"mTransportType\":1,\"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,\"mPassword\":\"\",\"mTLSAuthDirection\":\"\",\"mKeyPassword\":\"\",\"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\":{\"gatewayIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"portCount\":0,\"iatMode\":\"0\",\"cert\":\"CERT\",\"experimental\":false,\"portSeed\":0},\"type\":\"obfs4\",\"protocols\":[\"tcp\"],\"ports\":[\"1234\"]}},\"mProxyName\":\"\",\"mUseProxyAuth\":false,\"ConnectionAdapter.META_TYPE\":\"de.blinkt.openvpn.core.connection.Obfs4Connection\",\"mEnabled\":true}],\"mUseLzo\":false,\"mTransportType\":2,\"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,\"mPassword\":\"\",\"mTLSAuthDirection\":\"\",\"mKeyPassword\":\"\",\"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\":{\"gatewayIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"portCount\":0,\"iatMode\":\"1\",\"cert\":\"CERT\",\"experimental\":false,\"portSeed\":0},\"type\":\"obfs4\",\"protocols\":[\"tcp\"],\"ports\":[\"1234\"]}},\"mProxyName\":\"127.0.0.1\",\"mUseProxyAuth\":false,\"ConnectionAdapter.META_TYPE\":\"de.blinkt.openvpn.core.connection.Obfs4Connection\",\"mEnabled\":true}],\"mUseLzo\":false,\"mTransportType\":2,\"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,\"mPassword\":\"\",\"mTLSAuthDirection\":\"\",\"mKeyPassword\":\"\",\"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\":{\"gatewayIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"portCount\":0,\"iatMode\":\"1\",\"cert\":\"CERT\",\"experimental\":false,\"portSeed\":0},\"type\":\"obfs4\",\"protocols\":[\"kcp\"],\"ports\":[\"1234\"]}},\"mProxyName\":\"127.0.0.1\",\"mUseProxyAuth\":false,\"ConnectionAdapter.META_TYPE\":\"de.blinkt.openvpn.core.connection.Obfs4Connection\",\"mEnabled\":true}],\"mUseLzo\":false,\"mTransportType\":2,\"mAllowAppVpnBypass\":false,\"mUsePull\":true,\"mUseRandomHostname\":false,\"mAuthRetry\":0}\n"; + + private static final String OBFS4CONNECTION_PROFILE_OBFSVPN_HOP = "{\"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,\"mPassword\":\"\",\"mTLSAuthDirection\":\"\",\"mKeyPassword\":\"\",\"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\":{\"gatewayIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"portCount\":100,\"endpoints\":[{\"ip\":\"1.1.1.1\",\"cert\":\"CERT1\"},{\"ip\":\"2.2.2.2\",\"cert\":\"CERT2\"}],\"iatMode\":\"1\",\"experimental\":true,\"portSeed\":200},\"type\":\"obfs4\",\"protocols\":[\"tcp\"],\"ports\":[\"1234\"]}},\"mProxyName\":\"127.0.0.1\",\"mUseProxyAuth\":false,\"ConnectionAdapter.META_TYPE\":\"de.blinkt.openvpn.core.connection.Obfs4Connection\",\"mEnabled\":true}],\"mUseLzo\":false,\"mTransportType\":3,\"mAllowAppVpnBypass\":false,\"mUsePull\":true,\"mUseRandomHostname\":false,\"mAuthRetry\":0}\n"; + + private static final String OBFS4CONNECTION_PROFILE_OBFSVPN_HOP_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,\"mPassword\":\"\",\"mTLSAuthDirection\":\"\",\"mKeyPassword\":\"\",\"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\":{\"gatewayIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"portCount\":100,\"endpoints\":[{\"ip\":\"1.1.1.1\",\"cert\":\"CERT1\"},{\"ip\":\"2.2.2.2\",\"cert\":\"CERT2\"}],\"iatMode\":\"1\",\"experimental\":true,\"portSeed\":2500},\"type\":\"obfs4\",\"protocols\":[\"kcp\"],\"ports\":[\"1234\"]}},\"mProxyName\":\"127.0.0.1\",\"mUseProxyAuth\":false,\"ConnectionAdapter.META_TYPE\":\"de.blinkt.openvpn.core.connection.Obfs4Connection\",\"mEnabled\":true}],\"mUseLzo\":false,\"mTransportType\":3,\"mAllowAppVpnBypass\":false,\"mUsePull\":true,\"mUseRandomHostname\":false,\"mAuthRetry\":0}\n"; @Before public void setup() { @@ -71,7 +78,9 @@ public class VpnProfileTest { 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", false)); + + Transport transport = new Transport(OBFS4.toString(), new String[]{"tcp"}, new String[]{"1234"}, "CERT"); + mockVpnProfile.mConnections[0] = new Obfs4Connection(new Obfs4Options("192.168.0.1", transport)); mockVpnProfile.mLastUsed = 0; String s = mockVpnProfile.toJson(); System.out.println(s); @@ -88,7 +97,9 @@ public class VpnProfileTest { 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)); + Transport.Options options = new Transport.Options("CERT", "1"); + Transport transport = new Transport(OBFS4.toString(), new String[]{"tcp"}, new String[]{"1234"}, options); + mockVpnProfile.mConnections[0] = new Obfs4Connection(new Obfs4Options("192.168.0.1", transport)); mockVpnProfile.mLastUsed = 0; String s = mockVpnProfile.toJson(); System.out.println(s); @@ -106,7 +117,9 @@ public class VpnProfileTest { 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", true)); + Transport.Options options = new Transport.Options("CERT", "1"); + Transport transport = new Transport(OBFS4.toString(), new String[]{"kcp"}, new String[]{"1234"}, options); + mockVpnProfile.mConnections[0] = new Obfs4Connection(new Obfs4Options("192.168.0.1", transport)); mockVpnProfile.mLastUsed = 0; String s = mockVpnProfile.toJson(); System.out.println(s); @@ -119,6 +132,49 @@ public class VpnProfileTest { assertEquals(expectation.toString(),actual.toString()); } + @Test + public void toJson_obfs4hop_kcp() throws JSONException { + when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); + + VpnProfile mockVpnProfile = new VpnProfile("mockProfile", OBFS4_HOP); + + Transport.Options options = new Transport.Options("1", new Transport.Endpoint[]{new Transport.Endpoint("1.1.1.1", "CERT1"), new Transport.Endpoint("2.2.2.2", "CERT2")}, 2500, 100, true); + Transport transport = new Transport(OBFS4.toString(), new String[]{"kcp"}, new String[]{"1234"}, options); + mockVpnProfile.mConnections[0] = new Obfs4Connection(new Obfs4Options("192.168.0.1", transport)); + + 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_HOP_KCP); + + assertEquals(expectation.toString(),actual.toString()); + } + + @Test + public void toJson_obfs4hop() throws JSONException { + when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); + + VpnProfile mockVpnProfile = new VpnProfile("mockProfile", OBFS4_HOP); + Transport.Options options = new Transport.Options("1", new Transport.Endpoint[]{new Transport.Endpoint("1.1.1.1", "CERT1"), new Transport.Endpoint("2.2.2.2", "CERT2")}, 200, 100, true); + Transport transport = new Transport(OBFS4.toString(), new String[]{"tcp"}, new String[]{"1234"}, options); + mockVpnProfile.mConnections[0] = new Obfs4Connection(new Obfs4Options("192.168.0.1", transport)); + + 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_HOP); + + assertEquals(expectation.toString(),actual.toString()); + } + @Test public void fromJson_obfs4() { when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(false); @@ -130,11 +186,12 @@ public class VpnProfileTest { 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); + assertFalse(Arrays.asList(obfs4Connection.getObfs4Options().transport.getProtocols()).contains(UDP)); + assertEquals("CERT", obfs4Connection.getObfs4Options().transport.getOptions().getCert()); + assertEquals("0", obfs4Connection.getObfs4Options().transport.getOptions().getIatMode()); + assertEquals("192.168.0.1", obfs4Connection.getObfs4Options().gatewayIP); + assertEquals("1234", obfs4Connection.getObfs4Options().transport.getPorts()[0]); + assertEquals(1, obfs4Connection.getObfs4Options().transport.getPorts().length); } @Test @@ -148,11 +205,12 @@ public class VpnProfileTest { 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); + assertEquals("tcp", obfs4Connection.getObfs4Options().transport.getProtocols()[0]); + assertEquals("CERT", obfs4Connection.getObfs4Options().transport.getOptions().getCert()); + assertEquals("1", obfs4Connection.getObfs4Options().transport.getOptions().getIatMode()); + assertEquals("192.168.0.1", obfs4Connection.getObfs4Options().gatewayIP); + assertEquals("1234", obfs4Connection.getObfs4Options().transport.getPorts()[0]); + assertEquals(1, obfs4Connection.getObfs4Options().transport.getPorts().length); } @Test @@ -166,10 +224,10 @@ public class VpnProfileTest { 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); + assertEquals("kcp", obfs4Connection.getObfs4Options().transport.getProtocols()[0]); + assertEquals("CERT", obfs4Connection.getObfs4Options().transport.getOptions().getCert()); + assertEquals("1", obfs4Connection.getObfs4Options().transport.getOptions().getIatMode()); + assertEquals("192.168.0.1", obfs4Connection.getObfs4Options().gatewayIP); + assertEquals("1234", obfs4Connection.getObfs4Options().transport.getPorts()[0]); } } \ No newline at end of file diff --git a/app/src/test/java/se/leap/bitmaskclient/base/models/GatewayJsonTest.java b/app/src/test/java/se/leap/bitmaskclient/base/models/GatewayJsonTest.java index 61275378..801f98ad 100644 --- a/app/src/test/java/se/leap/bitmaskclient/base/models/GatewayJsonTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/base/models/GatewayJsonTest.java @@ -12,7 +12,7 @@ public class GatewayJsonTest { @Test public void testToString() { - String gatewayJSON = "{\"location\":\"Unknown Location\",\"ip_address\":\"1.2.3.4\",\"host\":\"pinned.obfuscation.proxy\",\"capabilities\":{\"adblock\":false,\"filter_dns\":false,\"limited\":false,\"transport\":[{\"type\":\"obfs4\",\"protocols\":[\"tcp\"],\"ports\":[\"1194\"],\"options\":{\"cert\":\"xxxxxxx\",\"iatMode\":\"0\"}}],\"user_ips\":false}}"; + String gatewayJSON = "{\"location\":\"Unknown Location\",\"ip_address\":\"1.2.3.4\",\"host\":\"pinned.obfuscation.proxy\",\"capabilities\":{\"adblock\":false,\"filter_dns\":false,\"limited\":false,\"transport\":[{\"type\":\"obfs4\",\"protocols\":[\"tcp\"],\"ports\":[\"1194\"],\"options\":{\"cert\":\"xxxxxxx\",\"iatMode\":\"0\",\"experimental\":false,\"portSeed\":0,\"portCount\":0}}],\"user_ips\":false}}"; Connection.TransportType transportType = OBFS4; Transport[] transports = new Transport[]{ diff --git a/app/src/test/java/se/leap/bitmaskclient/base/models/TransportTest.java b/app/src/test/java/se/leap/bitmaskclient/base/models/TransportTest.java new file mode 100644 index 00000000..37dfc161 --- /dev/null +++ b/app/src/test/java/se/leap/bitmaskclient/base/models/TransportTest.java @@ -0,0 +1,68 @@ +package se.leap.bitmaskclient.base.models; + +import static se.leap.bitmaskclient.base.models.Constants.CAPABILITIES; +import static se.leap.bitmaskclient.base.models.Constants.TRANSPORT; + +import junit.framework.TestCase; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; + +import de.blinkt.openvpn.core.connection.Connection; +import se.leap.bitmaskclient.testutils.TestSetupHelper; + +public class TransportTest extends TestCase { + + private JSONObject gateway; + + public void test_obfs4_fromJson() throws IOException, JSONException { + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_obfs4hop_tcp_gateways.json"))).getJSONArray("gateways").getJSONObject(2); + JSONObject obfs4Transport = gateway.getJSONObject(CAPABILITIES).getJSONArray(TRANSPORT).getJSONObject(1); + Transport transport = Transport.fromJson(obfs4Transport); + assertEquals("obfs4", transport.getType()); + assertEquals("0", transport.getOptions().getIatMode()); + assertEquals("kcp", transport.getProtocols()[0]); + assertEquals(1, transport.getProtocols().length); + assertEquals("23050", transport.getPorts()[0]); + assertEquals(1, transport.getPorts().length); + assertEquals("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1", transport.getOptions().getCert()); + assertNull(transport.getOptions().getEndpoints()); + assertEquals(0, transport.getOptions().getPortCount()); + assertEquals(0, transport.getOptions().getPortSeed()); + assertFalse(transport.getOptions().isExperimental()); + } + + public void test_obfs4hop_fromJson() throws IOException, JSONException { + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_obfs4hop_tcp_gateways.json"))).getJSONArray("gateways").getJSONObject(2); + JSONObject obfs4Transport = gateway.getJSONObject(CAPABILITIES).getJSONArray(TRANSPORT).getJSONObject(2); + Transport transport = Transport.fromJson(obfs4Transport); + assertEquals("obfs4-hop", transport.getType()); + assertEquals(Connection.TransportType.OBFS4_HOP, transport.getTransportType()); + assertEquals("tcp", transport.getProtocols()[0]); + assertEquals(1, transport.getProtocols().length); + assertNull(transport.getPorts()); + assertNull(transport.getOptions().getCert()); + assertNotNull(transport.getOptions().getEndpoints()); + assertEquals(2, transport.getOptions().getEndpoints().length); + assertEquals("CERT1", transport.getOptions().getEndpoints()[0].getCert()); + assertEquals("CERT2", transport.getOptions().getEndpoints()[1].getCert()); + assertEquals("1.1.1.1", transport.getOptions().getEndpoints()[0].getIp()); + assertEquals("2.2.2.2", transport.getOptions().getEndpoints()[1].getIp()); + assertTrue(transport.getOptions().isExperimental()); + } + + public void test_openvpn_fromJson() throws IOException, JSONException { + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_obfs4hop_tcp_gateways.json"))).getJSONArray("gateways").getJSONObject(2); + JSONObject obfs4Transport = gateway.getJSONObject(CAPABILITIES).getJSONArray(TRANSPORT).getJSONObject(0); + Transport transport = Transport.fromJson(obfs4Transport); + assertEquals("openvpn", transport.getType()); + assertEquals(2, transport.getProtocols().length); + assertEquals("tcp", transport.getProtocols()[0]); + assertEquals("udp", transport.getProtocols()[1]); + assertEquals(1, transport.getPorts().length); + assertEquals("1195", transport.getPorts()[0]); + assertNull(transport.getOptions()); + } +} \ No newline at end of file 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 6b1c5bf3..99e71f05 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java @@ -9,6 +9,7 @@ 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_HOP; 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; @@ -34,6 +35,7 @@ import java.util.HashMap; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.connection.Connection; import de.blinkt.openvpn.core.connection.Obfs4Connection; +import de.blinkt.openvpn.core.connection.Obfs4HopConnection; import se.leap.bitmaskclient.base.utils.ConfigHelper; import se.leap.bitmaskclient.testutils.MockHelper; import se.leap.bitmaskclient.testutils.TestSetupHelper; @@ -1696,9 +1698,8 @@ public class VpnConfigGeneratorTest { configuration.preferUDP = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); - assertTrue(vpnProfiles.containsKey(OBFS4) && ((Obfs4Connection)vpnProfiles.get(OBFS4).mConnections[0]).getDispatcherOptions().udp); + assertTrue(vpnProfiles.containsKey(OBFS4) && ((Obfs4Connection)vpnProfiles.get(OBFS4).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("kcp")); assertTrue(vpnProfiles.containsKey(OPENVPN)); - } @Test @@ -1715,16 +1716,20 @@ public class VpnConfigGeneratorTest { } @Test - public void testGenerateVpnProfile_ObfuscationPinningEnabled_obfs4AndOpenvpnProfile () throws Exception { + public void testGenerateVpnProfile_ObfuscationPinningNotEnabled_obfs4AndOpenvpnProfile () throws Exception { gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_kcp_gateways.json"))).getJSONArray("gateways").getJSONObject(1); generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_kcp_gateways.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; - configuration.useObfuscationPinning = true; + configuration.useObfuscationPinning = false; + configuration.obfuscationProxyPort = "443"; + configuration.obfuscationProxyIP = "5.6.7.8"; + configuration.obfuscationProxyCert = "asdfasdf"; + configuration.obfuscationProxyKCP = true; configuration.remoteGatewayIP = "1.2.3.4"; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); - assertFalse("has no openvpn profile", vpnProfiles.containsKey(OPENVPN)); + assertTrue("has openvpn profile", vpnProfiles.containsKey(OPENVPN)); assertTrue("has obfs4 profile", vpnProfiles.containsKey(OBFS4)); assertTrue("bridge is running KCP", vpnProfiles.get(OBFS4).mGatewayIp.equals("1.2.3.4")); @@ -1747,7 +1752,7 @@ public class VpnConfigGeneratorTest { assertFalse("has openvpn profile", vpnProfiles.containsKey(OPENVPN)); assertTrue("has obfs4 profile", vpnProfiles.containsKey(OBFS4)); assertTrue("bridge is pinned one", vpnProfiles.get(OBFS4).getTransportType() == OBFS4 && !vpnProfiles.get(OBFS4).mConnections[0].isUseUdp() ); - assertTrue("bridge is running KCP", ((Obfs4Connection) vpnProfiles.get(OBFS4).mConnections[0]).getDispatcherOptions().udp == false); + assertTrue("bridge is running TCP", ((Obfs4Connection) vpnProfiles.get(OBFS4).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("tcp")); } @Test @@ -1768,7 +1773,29 @@ public class VpnConfigGeneratorTest { assertFalse("has openvpn profile", vpnProfiles.containsKey(OPENVPN)); assertTrue("has no obfs4 profile", vpnProfiles.containsKey(OBFS4)); assertTrue("bridge is pinned one", vpnProfiles.get(OBFS4).getTransportType() == OBFS4 && vpnProfiles.get(OBFS4).mGatewayIp.equals("1.2.3.4")); - assertTrue("bridge is running KCP", ((Obfs4Connection) vpnProfiles.get(OBFS4).mConnections[0]).getDispatcherOptions().udp == true); + assertTrue("bridge is running KCP", ((Obfs4Connection) vpnProfiles.get(OBFS4).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("kcp")); + } + @Test + public void testGenerateVpnProfile_obfs4hop_tcp () throws Exception { + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_obfs4hop_tcp_gateways.json"))).getJSONArray("gateways").getJSONObject(2); + generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_obfs4hop_tcp_gateways.json"))).getJSONObject(OPENVPN_CONFIGURATION); + VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); + configuration.apiVersion = 3; + vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); + HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); + assertTrue(vpnProfiles.containsKey(OBFS4_HOP) && ((Obfs4HopConnection)vpnProfiles.get(OBFS4_HOP).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("tcp")); + assertTrue(vpnProfiles.containsKey(OPENVPN)); } + @Test + public void testGenerateVpnProfile_obfs4hop_kcp () throws Exception { + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_obfs4hop_kcp_gateways.json"))).getJSONArray("gateways").getJSONObject(2); + generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_obfs4hop_kcp_gateways.json"))).getJSONObject(OPENVPN_CONFIGURATION); + VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); + configuration.apiVersion = 3; + vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); + HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); + assertTrue(vpnProfiles.containsKey(OBFS4_HOP) && ((Obfs4HopConnection)vpnProfiles.get(OBFS4_HOP).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("kcp")); + assertTrue(vpnProfiles.containsKey(OPENVPN)); + } } \ No newline at end of file diff --git a/app/src/test/resources/ptdemo_obfs4hop_kcp_gateways.json b/app/src/test/resources/ptdemo_obfs4hop_kcp_gateways.json new file mode 100644 index 00000000..a56bc571 --- /dev/null +++ b/app/src/test/resources/ptdemo_obfs4hop_kcp_gateways.json @@ -0,0 +1,178 @@ +{ + "gateways":[ + { + "capabilities":{ + "adblock":false, + "filter_dns":false, + "limited":false, + "transport":[ + { + "type":"obfs4", + "protocols":[ + "tcp" + ], + "ports":[ + "23049" + ], + "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-hop", + "protocols":[ + "kcp" + ], + "options": { + "iatMode": "0", + "endpoints": [ + { + "ip": "1.1.1.1", + "cert": "/ntRNI6JYP7R6kGKldibKWj0aCsv96Hdu/jSGncPy+rcverCLI7Emod+vRkz61hM7F/udA" + }, + { + "ip": "2.2.2.2", + "cert": "/ntRNI6JYP7R6kGKldibKWj0aCsv96Hdu/jSGncPy+rcverCLI7Emod+vRkz61hM7F/udA" + } + ], + "port_seed": 94, + "port_count": 100, + "experimental": true + } + }, + { + "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", + "protocols":[ + "kcp" + ], + "ports":[ + "23050" + ], + "options": { + "cert": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1", + "iatMode": "0" + } + }, + { + "type":"obfs4-hop", + "protocols":[ + "kcp" + ], + "options": { + "iatMode": "0", + "endpoints": [ + { + "ip": "1.1.1.1", + "cert": "/ntRNI6JYP7R6kGKldibKWj0aCsv96Hdu/jSGncPy+rcverCLI7Emod+vRkz61hM7F/udA" + }, + { + "ip": "2.2.2.2", + "cert": "/ntRNI6JYP7R6kGKldibKWj0aCsv96Hdu/jSGncPy+rcverCLI7Emod+vRkz61hM7F/udA" + } + ], + "port_seed": 94, + "port_count": 100, + "experimental": true + } + } + ], + "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_obfs4hop_misconfigured_udp_gateway.json b/app/src/test/resources/ptdemo_obfs4hop_misconfigured_udp_gateway.json new file mode 100644 index 00000000..a56bc571 --- /dev/null +++ b/app/src/test/resources/ptdemo_obfs4hop_misconfigured_udp_gateway.json @@ -0,0 +1,178 @@ +{ + "gateways":[ + { + "capabilities":{ + "adblock":false, + "filter_dns":false, + "limited":false, + "transport":[ + { + "type":"obfs4", + "protocols":[ + "tcp" + ], + "ports":[ + "23049" + ], + "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-hop", + "protocols":[ + "kcp" + ], + "options": { + "iatMode": "0", + "endpoints": [ + { + "ip": "1.1.1.1", + "cert": "/ntRNI6JYP7R6kGKldibKWj0aCsv96Hdu/jSGncPy+rcverCLI7Emod+vRkz61hM7F/udA" + }, + { + "ip": "2.2.2.2", + "cert": "/ntRNI6JYP7R6kGKldibKWj0aCsv96Hdu/jSGncPy+rcverCLI7Emod+vRkz61hM7F/udA" + } + ], + "port_seed": 94, + "port_count": 100, + "experimental": true + } + }, + { + "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", + "protocols":[ + "kcp" + ], + "ports":[ + "23050" + ], + "options": { + "cert": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1", + "iatMode": "0" + } + }, + { + "type":"obfs4-hop", + "protocols":[ + "kcp" + ], + "options": { + "iatMode": "0", + "endpoints": [ + { + "ip": "1.1.1.1", + "cert": "/ntRNI6JYP7R6kGKldibKWj0aCsv96Hdu/jSGncPy+rcverCLI7Emod+vRkz61hM7F/udA" + }, + { + "ip": "2.2.2.2", + "cert": "/ntRNI6JYP7R6kGKldibKWj0aCsv96Hdu/jSGncPy+rcverCLI7Emod+vRkz61hM7F/udA" + } + ], + "port_seed": 94, + "port_count": 100, + "experimental": true + } + } + ], + "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_obfs4hop_tcp_gateways.json b/app/src/test/resources/ptdemo_obfs4hop_tcp_gateways.json new file mode 100644 index 00000000..6a748a4f --- /dev/null +++ b/app/src/test/resources/ptdemo_obfs4hop_tcp_gateways.json @@ -0,0 +1,178 @@ +{ + "gateways":[ + { + "capabilities":{ + "adblock":false, + "filter_dns":false, + "limited":false, + "transport":[ + { + "type":"obfs4", + "protocols":[ + "tcp" + ], + "ports":[ + "23049" + ], + "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-hop", + "protocols":[ + "tcp" + ], + "options": { + "iatMode": "0", + "endpoints": [ + { + "ip": "1.1.1.1", + "cert": "/ntRNI6JYP7R6kGKldibKWj0aCsv96Hdu/jSGncPy+rcverCLI7Emod+vRkz61hM7F/udA" + }, + { + "ip": "2.2.2.2", + "cert": "/ntRNI6JYP7R6kGKldibKWj0aCsv96Hdu/jSGncPy+rcverCLI7Emod+vRkz61hM7F/udA" + } + ], + "port_seed": 94, + "port_count": 100, + "experimental": true + } + }, + { + "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", + "protocols":[ + "kcp" + ], + "ports":[ + "23050" + ], + "options": { + "cert": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1", + "iatMode": "0" + } + }, + { + "type":"obfs4-hop", + "protocols":[ + "tcp" + ], + "options": { + "iatMode": "0", + "endpoints": [ + { + "ip": "1.1.1.1", + "cert": "CERT1" + }, + { + "ip": "2.2.2.2", + "cert": "CERT2" + } + ], + "port_seed": 94, + "port_count": 100, + "experimental": true + } + } + ], + "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 -- cgit v1.2.3 From 6aafd792efa3501dfe5e203c7587cd7089df888a Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 12 Apr 2023 09:46:45 +0200 Subject: fix sanity checks for the case of decoupled bridges --- .../leap/bitmaskclient/eip/VpnConfigGenerator.java | 4 +- .../bitmaskclient/eip/GatewaysManagerTest.java | 18 +++-- .../test/resources/decoupled_pt.eip-service.json | 79 ++++++++++++++++++++++ .../resources/ptdemo_misconfigured_gateway.json | 54 --------------- 4 files changed, 90 insertions(+), 65 deletions(-) create mode 100644 app/src/test/resources/decoupled_pt.eip-service.json delete mode 100644 app/src/test/resources/ptdemo_misconfigured_gateway.json (limited to 'app/src') 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 7229f7ff..853082be 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java @@ -418,7 +418,9 @@ public class VpnConfigGenerator { } Transport openvpnTransport = transports.get(OPENVPN); if (openvpnTransport == null) { - return false; + // the bridge seems to be to be decoupled from the gateway, we can't say if the openvpn gateway + // will support this PT and hope the admins configured the gateway correctly + return true; } String[] protocols = openvpnTransport.getProtocols(); 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 1b42b5d2..913a023a 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java @@ -92,14 +92,6 @@ public class GatewaysManagerTest { assertEquals(0, gatewaysManager.size()); } - @Test - public void testGatewayManagerFromCurrentProvider_misconfiguredProvider_noGateways() throws IOException, NullPointerException { - Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_misconfigured_gateway.json", null); - MockHelper.mockProviderObservable(provider); - GatewaysManager gatewaysManager = new GatewaysManager(mockContext); - assertEquals(0, gatewaysManager.size()); - } - @Test public void testGatewayManagerFromCurrentProvider_threeGateways() { Provider provider = getProvider(null, null, null, null,null, null, "ptdemo_three_mixed_gateways.json", null); @@ -551,8 +543,6 @@ public class GatewaysManagerTest { assertEquals(0.3, gatewaysManager.getLocation("Amsterdam").getAverageLoad(OPENVPN)); } - - @Test public void testGetLoadForLocation_() { MockHelper.mockProviderObservable(null); @@ -560,6 +550,14 @@ public class GatewaysManagerTest { assertEquals(GatewaysManager.Load.UNKNOWN, gatewaysManager.getLoadForLocation("unknown city", OPENVPN)); } + @Test + public void testGatewayManagerFromCurrentProvider_decoupledBridges_twoGateways() throws IOException, NullPointerException { + Provider provider = getProvider(null, null, null, null, null, null, "decoupled_pt.eip-service.json", null); + MockHelper.mockProviderObservable(provider); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + assertEquals(2, gatewaysManager.size()); + } + private String getJsonStringFor(String filename) throws IOException { return TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream(filename)); } diff --git a/app/src/test/resources/decoupled_pt.eip-service.json b/app/src/test/resources/decoupled_pt.eip-service.json new file mode 100644 index 00000000..62dd0cf2 --- /dev/null +++ b/app/src/test/resources/decoupled_pt.eip-service.json @@ -0,0 +1,79 @@ +{ + "gateways":[ + { + "capabilities":{ + "adblock":false, + "filter_dns":false, + "limited":false, + "transport":[ + { + "ports":[ + "53", + "80", + "1194" + ], + "protocols":[ + "tcp", + "udp" + ], + "type":"openvpn" + } + ] + }, + "host":"gateway-nyc1-01.bitmask-dev.leapvpn.net", + "ip_address":"67.205.131.206", + "location":"NYC" + }, + { + "capabilities":{ + "adblock":false, + "filter_dns":false, + "limited":false, + "transport":[ + { + "options":{ + "cert":"pIp+etK+ud3aKAa3Uujbc3q3kwQOBQgW1UUIXXkeQ+i24WFnSzTT1Q69F0x8EXpcq5jyGg", + "iatMode":"0" + }, + "ports":[ + "443" + ], + "protocols":[ + "tcp" + ], + "type":"obfs4" + } + ] + }, + "host":"bridge-nyc1-01.bitmask-dev.leapvpn.net", + "ip_address":"104.248.232.240", + "location":"NYC" + } + ], + "locations":{ + "NYC":{ + "country_code":"US", + "hemisphere":"N", + "name":"New York City", + "timezone":"-4" + } + }, + "openvpn_configuration":{ + "auth":"SHA512", + "cipher":"AES-256-GCM", + "data-ciphers":"AES-256-GCM", + "dev":"tun", + "float":"", + "keepalive":"10 30", + "key-direction":"1", + "nobind":true, + "persist-key":true, + "rcvbuf":"0", + "sndbuf":"0", + "tls-cipher":"TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384", + "tls-version-min":"1.2", + "verb":"3" + }, + "serial":3, + "version":3 +} \ No newline at end of file diff --git a/app/src/test/resources/ptdemo_misconfigured_gateway.json b/app/src/test/resources/ptdemo_misconfigured_gateway.json deleted file mode 100644 index 38008880..00000000 --- a/app/src/test/resources/ptdemo_misconfigured_gateway.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "gateways":[ - { - "capabilities":{ - "adblock":false, - "filter_dns":false, - "limited":false, - "transport":[ - { - "type":"obfs4", - "protocols":[ - "tcp" - ], - "ports":[ - "443" - ], - "options": { - "cert": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX2", - "iat-mode": "0" - } - } - ], - "user_ips":false - }, - "host":"moscow.bitmask.net", - "ip_address":"3.21.247.89", - "location":"moscow" - } - ], - "locations":{ - "Amsterdam":{ - "country_code":"NL", - "hemisphere":"N", - "name":"Amsterdam", - "timezone":"-1" - } - }, - "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 -- cgit v1.2.3 From 6dd7821cd38c69b9fdc3172762744f32dcbf9606 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 13 Apr 2023 13:42:44 +0200 Subject: obfs4-hop enum value - improve handling of disallowed dashes in enums --- .../blinkt/openvpn/core/connection/Connection.java | 6 ++++ .../java/de/blinkt/openvpn/VpnProfileTest.java | 24 ++++++++++++++++ .../openvpn/core/connection/ConnectionTest.java | 33 ++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 app/src/test/java/de/blinkt/openvpn/core/connection/ConnectionTest.java (limited to 'app/src') 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 6dc78a30..0b28cbca 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 @@ -55,8 +55,11 @@ public abstract class Connection implements Serializable, Cloneable { return protocol; } } + + // !! Never use valueOf() to instantiate this enum, use fromString() !! public enum TransportType { OBFS4("obfs4"), + // dashes are not allowed in enums, so obfs4-hop becomes obfs4Hop -.- OBFS4_HOP("obfs4Hop"), OPENVPN("openvpn"), @@ -70,6 +73,9 @@ public abstract class Connection implements Serializable, Cloneable { @Override public String toString() { + if (this == OBFS4_HOP) { + return "obfs4-hop"; + } return transport; } diff --git a/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java b/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java index e5f9a9ca..a3ac3f92 100644 --- a/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java +++ b/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java @@ -40,6 +40,8 @@ public class VpnProfileTest { private static final String OBFS4CONNECTION_PROFILE_OBFSVPN_HOP_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,\"mPassword\":\"\",\"mTLSAuthDirection\":\"\",\"mKeyPassword\":\"\",\"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\":{\"gatewayIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"portCount\":100,\"endpoints\":[{\"ip\":\"1.1.1.1\",\"cert\":\"CERT1\"},{\"ip\":\"2.2.2.2\",\"cert\":\"CERT2\"}],\"iatMode\":\"1\",\"experimental\":true,\"portSeed\":2500},\"type\":\"obfs4\",\"protocols\":[\"kcp\"],\"ports\":[\"1234\"]}},\"mProxyName\":\"127.0.0.1\",\"mUseProxyAuth\":false,\"ConnectionAdapter.META_TYPE\":\"de.blinkt.openvpn.core.connection.Obfs4Connection\",\"mEnabled\":true}],\"mUseLzo\":false,\"mTransportType\":3,\"mAllowAppVpnBypass\":false,\"mUsePull\":true,\"mUseRandomHostname\":false,\"mAuthRetry\":0}\n"; + private static final String OBFS4CONNECTION_PROFILE_OBFSVPN_PORTHOPPING = "{\"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,\"mPassword\":\"\",\"mTLSAuthDirection\":\"\",\"mKeyPassword\":\"\",\"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\":true,\"mProxyPort\":\"8080\",\"mUseCustomConfig\":false,\"options\":{\"gatewayIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"portCount\":100,\"iatMode\":\"1\",\"cert\":\"CERT\",\"experimental\":true,\"portSeed\":200},\"type\":\"obfs4-hop\",\"protocols\":[\"tcp\"],\"ports\":[\"1234\"]}},\"mProxyName\":\"127.0.0.1\",\"mUseProxyAuth\":false,\"ConnectionAdapter.META_TYPE\":\"de.blinkt.openvpn.core.connection.Obfs4Connection\",\"mEnabled\":true}],\"mUseLzo\":false,\"mTransportType\":3,\"mAllowAppVpnBypass\":false,\"mUsePull\":true,\"mUseRandomHostname\":false,\"mAuthRetry\":0}\n"; + @Before public void setup() { mockStatic(UUID.class); @@ -154,6 +156,28 @@ public class VpnProfileTest { assertEquals(expectation.toString(),actual.toString()); } + @Test + public void toJson_obfs4hop_portHopping() throws JSONException { + when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); + + VpnProfile mockVpnProfile = new VpnProfile("mockProfile", OBFS4_HOP); + + Transport.Options options = new Transport.Options("1", null, "CERT",200, 100, true); + Transport transport = new Transport(OBFS4_HOP.toString(), new String[]{"tcp"}, new String[]{"1234"}, options); + mockVpnProfile.mConnections[0] = new Obfs4Connection(new Obfs4Options("192.168.0.1", transport)); + + 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_PORTHOPPING); + + assertEquals(expectation.toString(),actual.toString()); + } + @Test public void toJson_obfs4hop() throws JSONException { when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); diff --git a/app/src/test/java/de/blinkt/openvpn/core/connection/ConnectionTest.java b/app/src/test/java/de/blinkt/openvpn/core/connection/ConnectionTest.java new file mode 100644 index 00000000..6313bc82 --- /dev/null +++ b/app/src/test/java/de/blinkt/openvpn/core/connection/ConnectionTest.java @@ -0,0 +1,33 @@ +package de.blinkt.openvpn.core.connection; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_HOP; + +import org.junit.Test; + +public class ConnectionTest { + + @Test + public void TransportTypeTest_fromString() { + Connection.TransportType transportType = Connection.TransportType.fromString("obfs4-hop"); + assertEquals(OBFS4_HOP, transportType); + } + + @Test + public void TransportTypeTest_toString() { + assertEquals("obfs4-hop", OBFS4_HOP.toString()); + } + + @Test + public void TransportTypeTest_valueOf() { + Exception exception = null; + try { + Connection.TransportType.valueOf("obfs4-hop"); + } catch (IllegalArgumentException e) { + exception = e; + } + assertNotNull(exception); + } + +} -- cgit v1.2.3 From 2f32f3331b609d8a0324726f173e1aacf8854808 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 13 Apr 2023 13:44:47 +0200 Subject: set correct proxy port for obfs4-hop pt proxy --- .../openvpn/core/connection/Obfs4Connection.java | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'app/src') 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 73bfccdc..d152031a 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 @@ -4,6 +4,7 @@ import static se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper.useObf import static se.leap.bitmaskclient.pluggableTransports.ShapeshifterClient.DISPATCHER_IP; import static se.leap.bitmaskclient.pluggableTransports.ShapeshifterClient.DISPATCHER_PORT; +import se.leap.bitmaskclient.pluggableTransports.HoppingObfsVpnClient; import se.leap.bitmaskclient.pluggableTransports.Obfs4Options; import se.leap.bitmaskclient.pluggableTransports.ObfsVpnClient; @@ -22,18 +23,29 @@ public class Obfs4Connection extends Connection { setServerName(options.gatewayIP); setServerPort(options.transport.getPorts()[0]); setProxyName(ObfsVpnClient.SOCKS_IP); - setProxyPort(String.valueOf(ObfsVpnClient.SOCKS_PORT.get())); setProxyType(ProxyType.SOCKS5); + switch (options.transport.getTransportType()) { + case OBFS4: + setUseUdp(false); + setProxyPort(String.valueOf(ObfsVpnClient.SOCKS_PORT.get())); + break; + case OBFS4_HOP: + setUseUdp(true); + setProxyPort(String.valueOf(HoppingObfsVpnClient.PORT)); + break; + default:break; + } } 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); } - // while udp/kcp might be used on the wire, - // we don't use udp for openvpn in case of a obfs4 connection - setUseUdp(false); setProxyAuthUser(null); setProxyAuthPassword(null); setUseProxyAuth(false); -- cgit v1.2.3 From 9e7317c9e8323c0a97bca05548928ab0a5f0900d Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 13 Apr 2023 13:52:53 +0200 Subject: Assume port hopping only in case of a missing endpoints json for obfs4-hop. The gateway IP and the cert json field in options substitute the missing endpoints json --- .../leap/bitmaskclient/eip/VpnConfigGenerator.java | 32 ++++++++++++++++------ .../pluggableTransports/HoppingConfig.java | 20 ++++++++++---- .../pluggableTransports/HoppingObfsVpnClient.java | 8 +----- 3 files changed, 38 insertions(+), 22 deletions(-) (limited to 'app/src') 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 853082be..d32d1a71 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java @@ -228,7 +228,7 @@ public class VpnConfigGenerator { } private String gatewayConfiguration(TransportType transportType) { - String remotes = ""; + String configs = ""; StringBuilder stringBuilder = new StringBuilder(); try { @@ -257,12 +257,12 @@ public class VpnConfigGenerator { e.printStackTrace(); } - remotes = stringBuilder.toString(); - if (remotes.endsWith(newLine)) { - remotes = remotes.substring(0, remotes.lastIndexOf(newLine)); + configs = stringBuilder.toString(); + if (configs.endsWith(newLine)) { + configs = configs.substring(0, configs.lastIndexOf(newLine)); } - return remotes; + return configs; } private void gatewayConfigMinApiv3(TransportType transportType, StringBuilder stringBuilder, String[] ipAddresses) throws JSONException { @@ -372,7 +372,9 @@ public class VpnConfigGenerator { } if (transportType == OBFS4_HOP && - (transport.getOptions() == null || transport.getOptions().getEndpoints() == null || transport.getOptions().getPortCount() == 0)) { + (transport.getOptions() == null || + (transport.getOptions().getEndpoints() == null && transport.getOptions().getCert() == null) || + transport.getOptions().getPortCount() == 0)) { VpnStatus.logError("Misconfigured provider: missing properties for transport " + transport.getType() + " on gateway " + ipAddress); return; } @@ -403,10 +405,22 @@ public class VpnConfigGenerator { if (useObfuscationPinning) { return "route " + obfuscationPinningIP + " 255.255.255.255 net_gateway" + newLine; } - if (transport.getTransportType() == OBFS4) { - return "route " + ipAddress + " 255.255.255.255 net_gateway" + newLine; + switch (transport.getTransportType()) { + case OBFS4: + return "route " + ipAddress + " 255.255.255.255 net_gateway" + newLine; + case OBFS4_HOP: + if (transport.getOptions().getEndpoints() != null) { + StringBuilder routes = new StringBuilder(); + for (Transport.Endpoint endpoint : transport.getOptions().getEndpoints()) { + routes.append("route " + endpoint.getIp() + " 255.255.255.255 net_gateway" + newLine); + } + return routes.toString(); + } else { + return "route " + ipAddress + " 255.255.255.255 net_gateway" + newLine; + } } - return newLine; + + return ""; } // While openvpn in TCP mode is required for obfs4, openvpn in UDP mode is required for obfs4-hop diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingConfig.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingConfig.java index e885166a..3780b7dc 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingConfig.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingConfig.java @@ -20,17 +20,25 @@ public class HoppingConfig { public HoppingConfig(boolean kcp, String proxyAddr, - Transport transport, + Obfs4Options options, int minHopSeconds, int hopJitter) { this.kcp = kcp; this.proxyAddr = proxyAddr; + Transport transport = options.transport; Transport.Endpoint[] endpoints = transport.getOptions().getEndpoints(); - this.remotes = new String[endpoints.length]; - this.certs = new String[endpoints.length]; - for (int i = 0; i < remotes.length; i++) { - remotes[i] = endpoints[i].getIp(); - certs[i] = endpoints[i].getCert(); + if (endpoints == null) { + // only port hopping, we assume the gateway IP as hopping PT's IP + this.remotes = new String[]{ options.gatewayIP }; + this.certs = new String[] { transport.getOptions().getCert() }; + } else { + // port+ip hopping + this.remotes = new String[endpoints.length]; + this.certs = new String[endpoints.length]; + for (int i = 0; i < remotes.length; i++) { + remotes[i] = endpoints[i].getIp(); + certs[i] = endpoints[i].getCert(); + } } this.portSeed = transport.getOptions().getPortSeed(); this.portCount = transport.getOptions().getPortCount(); diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingObfsVpnClient.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingObfsVpnClient.java index 1b19213f..751208ba 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingObfsVpnClient.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingObfsVpnClient.java @@ -1,7 +1,5 @@ package se.leap.bitmaskclient.pluggableTransports; -import static de.blinkt.openvpn.core.connection.Connection.TransportProtocol.KCP; - import client.Client; import client.HopClient; import de.blinkt.openvpn.core.VpnStatus; @@ -21,11 +19,7 @@ public class HoppingObfsVpnClient implements PtClientInterface { //if so, we stick to it, otherwise we flip the flag boolean kcp = Constants.KCP.equals(options.transport.getProtocols()[0]); - if (options.transport.getOptions().getEndpoints() == null) { - throw new IllegalStateException("No Endpoints for hopping pt detected!"); - } - - HoppingConfig hoppingConfig = new HoppingConfig(kcp,IP+":"+PORT, options.transport, 10, 10); + HoppingConfig hoppingConfig = new HoppingConfig(kcp,IP+":"+PORT, options, 10, 10); try { client = Client.newFFIHopClient(hoppingConfig.toString()); } catch (Exception e) { -- cgit v1.2.3 From 649e6487fb045ab8df0144579f74ab7123e2cbc6 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 13 Apr 2023 14:37:45 +0200 Subject: improve sanity check for obfs4 pts defined in eip-service.json --- app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/src') 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 d32d1a71..4d90798d 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java @@ -366,7 +366,7 @@ public class VpnConfigGenerator { } TransportType transportType = transport.getTransportType(); - if (transportType == OBFS4 && transport.getPorts() == null) { + if (transportType == OBFS4 && (transport.getPorts() == null || transport.getPorts().length == 0)) { VpnStatus.logError("Misconfigured provider: no ports defined in " + transport.getType() + " transport JSON for gateway " + ipAddress); return; } -- cgit v1.2.3 From bb2fff07c050e24b2291ca13575f1b1cfa6085c1 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 13 Apr 2023 14:38:18 +0200 Subject: add required extra options for obfs4-hop pt --- .../java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'app/src') 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 4d90798d..90ffb6b0 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java @@ -381,6 +381,7 @@ public class VpnConfigGenerator { stringBuilder.append(getRouteString(ipAddress, transport)); stringBuilder.append(getRemoteString(ipAddress, transport)); + stringBuilder.append(getExtraOptions(transport)); } public String getRemoteString(String ipAddress, Transport transport) { @@ -401,6 +402,15 @@ public class VpnConfigGenerator { return REMOTE + " " + DISPATCHER_IP + " " + DISPATCHER_PORT + " tcp" + newLine; } + public String getExtraOptions(Transport transport) { + if (transport.getTransportType() == OBFS4_HOP) { + return "replay-window 65535" + newLine + + "ping-restart 300" + newLine + + "tun-mtu 48000" + newLine; + } + return ""; + } + public String getRouteString(String ipAddress, Transport transport) { if (useObfuscationPinning) { return "route " + obfuscationPinningIP + " 255.255.255.255 net_gateway" + newLine; -- cgit v1.2.3 From a51dbb9a0bb51441786cf5763d5f0d014e803b69 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 13 Apr 2023 14:39:48 +0200 Subject: adding tests --- .../leap/bitmaskclient/base/models/Transport.java | 6 + .../bitmaskclient/eip/GatewaysManagerTest.java | 2 +- .../bitmaskclient/eip/VpnConfigGeneratorTest.java | 329 +++++++++++++++++++++ .../test/resources/decoupled_pt.eip-service.json | 41 +++ .../decoupled_pt_portHopping.eip-service.json | 107 +++++++ 5 files changed, 484 insertions(+), 1 deletion(-) create mode 100644 app/src/test/resources/decoupled_pt_portHopping.eip-service.json (limited to 'app/src') diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Transport.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Transport.java index 7d9b61a7..33fbbf7a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/models/Transport.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Transport.java @@ -89,13 +89,19 @@ public class Transport implements Serializable { } public Options(String iatMode, Endpoint[] endpoints, int portSeed, int portCount, boolean experimental) { + this(iatMode, endpoints, null, portSeed, portCount, experimental); + } + + public Options(String iatMode, Endpoint[] endpoints, String cert, int portSeed, int portCount, boolean experimental) { this.iatMode = iatMode; this.endpoints = endpoints; this.portSeed = portSeed; this.portCount = portCount; this.experimental = experimental; + this.cert = cert; } + @Nullable public String getCert() { return cert; 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 913a023a..998ed2fd 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java @@ -555,7 +555,7 @@ public class GatewaysManagerTest { Provider provider = getProvider(null, null, null, null, null, null, "decoupled_pt.eip-service.json", null); MockHelper.mockProviderObservable(provider); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); - assertEquals(2, gatewaysManager.size()); + assertEquals(3, gatewaysManager.size()); } private String getJsonStringFor(String filename) throws IOException { 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 99e71f05..565e6396 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java @@ -1420,6 +1420,266 @@ public class VpnConfigGeneratorTest { "rcvbuf 0 \n" + "tls-version-min 1.2 \n"; + String expectedVPNConfig_hopping_pt_portHopping = "# 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 127.0.0.1 8080 udp\n" + + "\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" + + "\n" + + "\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" + + "\n" + + "\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" + + "\n" + + "# crl-verify file missing in config profile\n" + + "route 192.81.208.164 255.255.255.255 net_gateway\n"+ + "tun-mtu 48000\n"+ + "nobind\n"+ + "remote-cert-tls server\n" + + "data-ciphers AES-256-GCM\n" + + "cipher AES-256-GCM\n" + + "auth SHA512\n" + + "float\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" + + "keepalive 10 30 \n" + + "replay-window 65535 \n" + + "sndbuf 0 \n" + + "rcvbuf 0 \n" + + "tls-version-min 1.2 \n" + + "ping-restart 300 \n" + + "tls-cipher TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384 \n"; + + String expectedVPNConfig_hopping_pt_portAndIpHopping = "# 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 127.0.0.1 8080 udp\n" + + "\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" + + "\n" + + "\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" + + "\n" + + "\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" + + "\n" + + "# crl-verify file missing in config profile\n" + + "route 192.81.208.164 255.255.255.255 net_gateway\n"+ + "route 192.81.208.165 255.255.255.255 net_gateway\n"+ + "route 192.81.208.166 255.255.255.255 net_gateway\n"+ + "tun-mtu 48000\n"+ + "nobind\n"+ + "remote-cert-tls server\n" + + "data-ciphers AES-256-GCM\n" + + "cipher AES-256-GCM\n" + + "auth SHA512\n" + + "float\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" + + "keepalive 10 30 \n" + + "replay-window 65535 \n" + + "sndbuf 0 \n" + + "rcvbuf 0 \n" + + "tls-version-min 1.2 \n" + + "ping-restart 300 \n" + + "tls-cipher TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384 \n"; + @Before public void setUp() throws Exception { @@ -1798,4 +2058,73 @@ public class VpnConfigGeneratorTest { assertTrue(vpnProfiles.containsKey(OBFS4_HOP) && ((Obfs4HopConnection)vpnProfiles.get(OBFS4_HOP).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("kcp")); assertTrue(vpnProfiles.containsKey(OPENVPN)); } + + @Test + public void testGetConfigFile_testHoppingPtPortHopping_decoupled() throws Exception { + when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt_portHopping.eip-service.json"))).getJSONArray("gateways").getJSONObject(2); + generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt_portHopping.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); + VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); + configuration.apiVersion = 3; + vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); + HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); + System.out.println(vpnProfiles.get(OBFS4_HOP).getConfigFile(context, false)); + assertEquals(expectedVPNConfig_hopping_pt_portHopping.trim(), vpnProfiles.get(OBFS4_HOP).getConfigFile(context, false).trim()); + } + + @Test + public void testGetConfigFile_testHoppingPtPortAndIPHopping_decoupled() throws Exception { + when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt_portHopping.eip-service.json"))).getJSONArray("gateways").getJSONObject(2); + generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt_portHopping.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); + VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); + configuration.apiVersion = 3; + vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); + HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); + System.out.println(vpnProfiles.get(OBFS4_HOP).getConfigFile(context, false)); + assertEquals(expectedVPNConfig_hopping_pt_portHopping.trim(), vpnProfiles.get(OBFS4_HOP).getConfigFile(context, false).trim()); + } + @Test + public void testGenerateVpnProfile_obfs4_decoupled() throws Exception { + when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt.eip-service.json"))).getJSONArray("gateways").getJSONObject(1); + generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); + VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); + configuration.apiVersion = 3; + vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); + HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); + assertTrue(vpnProfiles.containsKey(OBFS4)); + assertTrue(((Obfs4Connection)vpnProfiles.get(OBFS4).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("tcp")); + assertFalse(vpnProfiles.containsKey(OPENVPN)); + } + + @Test + public void testGenerateVpnProfile_obfs4hop_decoupled() throws Exception { + when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt.eip-service.json"))).getJSONArray("gateways").getJSONObject(2); + generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); + VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); + configuration.apiVersion = 3; + vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); + HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); + assertTrue(vpnProfiles.containsKey(OBFS4_HOP)); + assertTrue(((Obfs4HopConnection)vpnProfiles.get(OBFS4_HOP).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("tcp")); + assertFalse(vpnProfiles.containsKey(OPENVPN)); + assertFalse(vpnProfiles.containsKey(OBFS4)); + } + + @Test + public void testGenerateVpnProfile_obfs4hop_onlyPortHopping_decoupled() throws Exception { + when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt_portHopping.eip-service.json"))).getJSONArray("gateways").getJSONObject(2); + generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt_portHopping.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); + VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); + configuration.apiVersion = 3; + vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); + HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); + assertTrue(vpnProfiles.containsKey(OBFS4_HOP)); + assertTrue(((Obfs4HopConnection)vpnProfiles.get(OBFS4_HOP).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("tcp")); + assertFalse(vpnProfiles.containsKey(OPENVPN)); + assertFalse(vpnProfiles.containsKey(OBFS4)); + } } \ No newline at end of file diff --git a/app/src/test/resources/decoupled_pt.eip-service.json b/app/src/test/resources/decoupled_pt.eip-service.json index 62dd0cf2..e2ad50a1 100644 --- a/app/src/test/resources/decoupled_pt.eip-service.json +++ b/app/src/test/resources/decoupled_pt.eip-service.json @@ -48,6 +48,47 @@ "host":"bridge-nyc1-01.bitmask-dev.leapvpn.net", "ip_address":"104.248.232.240", "location":"NYC" + }, + { + "capabilities":{ + "adblock":false, + "filter_dns":false, + "limited":false, + "transport":[ + { + "options":{ + "endpoints": [ + { + "cert" : "CERT", + "ip": "192.81.208.164" + }, + { + "cert" : "CERT2", + "ip": "192.81.208.165" + }, + { + "cert" : "CERT3", + "ip": "192.81.208.166" + } + ], + "experimental":true, + "iatMode":"0", + "port_count":100, + "port_seed":0 + }, + "ports":[ + + ], + "protocols":[ + "tcp" + ], + "type":"obfs4-hop" + } + ] + }, + "host":"bridge-nyc1-02.bitmask-dev.leapvpn.net", + "ip_address":"192.81.208.164", + "location":"NYC" } ], "locations":{ diff --git a/app/src/test/resources/decoupled_pt_portHopping.eip-service.json b/app/src/test/resources/decoupled_pt_portHopping.eip-service.json new file mode 100644 index 00000000..a9504454 --- /dev/null +++ b/app/src/test/resources/decoupled_pt_portHopping.eip-service.json @@ -0,0 +1,107 @@ +{ + "gateways":[ + { + "capabilities":{ + "adblock":false, + "filter_dns":false, + "limited":false, + "transport":[ + { + "ports":[ + "53", + "80", + "1194" + ], + "protocols":[ + "tcp", + "udp" + ], + "type":"openvpn" + } + ] + }, + "host":"gateway-nyc1-01.bitmask-dev.leapvpn.net", + "ip_address":"67.205.131.206", + "location":"NYC" + }, + { + "capabilities":{ + "adblock":false, + "filter_dns":false, + "limited":false, + "transport":[ + { + "options":{ + "cert":"CERT", + "iatMode":"0" + }, + "ports":[ + "443" + ], + "protocols":[ + "tcp" + ], + "type":"obfs4" + } + ] + }, + "host":"bridge-nyc1-01.bitmask-dev.leapvpn.net", + "ip_address":"104.248.232.240", + "location":"NYC" + }, + { + "capabilities":{ + "adblock":false, + "filter_dns":false, + "limited":false, + "transport":[ + { + "options":{ + "cert":"CERT", + "experimental":true, + "iatMode":"0", + "port_count":100, + "port_seed":0 + }, + "ports":[ + + ], + "protocols":[ + "tcp" + ], + "type":"obfs4-hop" + } + ] + }, + "host":"bridge-nyc1-02.bitmask-dev.leapvpn.net", + "ip_address":"192.81.208.164", + "location":"NYC" + } + ], + "locations":{ + "NYC":{ + "country_code":"US", + "hemisphere":"N", + "name":"New York City", + "timezone":"-4" + } + }, + "openvpn_configuration":{ + "auth":"SHA512", + "cipher":"AES-256-GCM", + "data-ciphers":"AES-256-GCM", + "dev":"tun", + "float":"", + "keepalive":"10 30", + "key-direction":"1", + "nobind":true, + "persist-key":true, + "rcvbuf":"0", + "sndbuf":"0", + "tls-cipher":"TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384", + "tls-version-min":"1.2", + "verb":"3" + }, + "serial":3, + "version":3 +} \ No newline at end of file -- cgit v1.2.3 From 601a77ad059b3da754c3baaf10b01dbc19b927be Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 15 Apr 2023 13:06:53 +0200 Subject: fix missing domain name of manually added providers in provider selection --- .../leap/bitmaskclient/base/models/Provider.java | 7 ++- .../base/utils/InputStreamHelper.java | 12 +---- .../providersetup/ProviderManager.java | 58 ++++++++++++---------- .../activities/ProviderListBaseActivity.java | 2 +- .../leap/bitmaskclient/testutils/MockHelper.java | 1 - 5 files changed, 39 insertions(+), 41 deletions(-) (limited to 'app/src') 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 57653263..08e13cf6 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 @@ -126,8 +126,10 @@ public final class Provider implements Parcelable { setGeoipUrl(geoipUrl); } - public Provider(String mainUrl, String providerIp, String providerApiIp) { - this(mainUrl, null, null, providerIp, providerApiIp); + public static Provider createCustomProvider(String mainUrl, String domain) { + Provider p = new Provider(mainUrl); + p.domain = domain; + return p; } public Provider(String mainUrl, String geoipUrl, String motdUrl, String providerIp, String providerApiIp) { @@ -520,6 +522,7 @@ public final class Provider implements Parcelable { JSONObject json = new JSONObject(); try { json.put(Provider.MAIN_URL, mainUrl); + json.put(Provider.DOMAIN, domain); } catch (JSONException e) { e.printStackTrace(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java index 8a526499..8e6273a7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java @@ -23,22 +23,12 @@ public class InputStreamHelper { return s.hasNext() ? s.next() : ""; } - public static String extractKeyFromInputStream(InputStream inputStream, String key) { - String value = ""; - - JSONObject fileContents = inputStreamToJson(inputStream); - if (fileContents != null) - value = fileContents.optString(key); - return value; - } - public static JSONObject inputStreamToJson(InputStream inputStream) { - JSONObject json = null; + JSONObject json = new JSONObject(); try { byte[] bytes = new byte[inputStream.available()]; if (inputStream.read(bytes) > 0) json = new JSONObject(new String(bytes)); - inputStream.reset(); } catch (IOException | JSONException e) { e.printStackTrace(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java index 1ae2a033..775e174a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java @@ -1,11 +1,28 @@ package se.leap.bitmaskclient.providersetup; +import static se.leap.bitmaskclient.base.models.Constants.EXT_JSON; +import static se.leap.bitmaskclient.base.models.Constants.EXT_PEM; +import static se.leap.bitmaskclient.base.models.Constants.URLS; +import static se.leap.bitmaskclient.base.models.Provider.DOMAIN; +import static se.leap.bitmaskclient.base.models.Provider.GEOIP_URL; +import static se.leap.bitmaskclient.base.models.Provider.MAIN_URL; +import static se.leap.bitmaskclient.base.models.Provider.MOTD_URL; +import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_API_IP; +import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_IP; +import static se.leap.bitmaskclient.base.utils.FileHelper.createFile; +import static se.leap.bitmaskclient.base.utils.FileHelper.persistFile; +import static se.leap.bitmaskclient.base.utils.InputStreamHelper.getInputStreamFrom; +import static se.leap.bitmaskclient.base.utils.InputStreamHelper.inputStreamToJson; +import static se.leap.bitmaskclient.base.utils.InputStreamHelper.loadInputStreamAsString; + import android.content.res.AssetManager; import androidx.annotation.VisibleForTesting; import com.pedrogomez.renderers.AdapteeCollection; +import org.json.JSONObject; + import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -19,20 +36,6 @@ import java.util.Set; import se.leap.bitmaskclient.base.models.Provider; -import static se.leap.bitmaskclient.base.models.Constants.EXT_JSON; -import static se.leap.bitmaskclient.base.models.Constants.EXT_PEM; -import static se.leap.bitmaskclient.base.models.Constants.URLS; -import static se.leap.bitmaskclient.base.models.Provider.GEOIP_URL; -import static se.leap.bitmaskclient.base.models.Provider.MAIN_URL; -import static se.leap.bitmaskclient.base.models.Provider.MOTD_URL; -import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_API_IP; -import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_IP; -import static se.leap.bitmaskclient.base.utils.FileHelper.createFile; -import static se.leap.bitmaskclient.base.utils.FileHelper.persistFile; -import static se.leap.bitmaskclient.base.utils.InputStreamHelper.extractKeyFromInputStream; -import static se.leap.bitmaskclient.base.utils.InputStreamHelper.getInputStreamFrom; -import static se.leap.bitmaskclient.base.utils.InputStreamHelper.loadInputStreamAsString; - /** * Created by parmegv on 4/12/14. */ @@ -96,11 +99,14 @@ public class ProviderManager implements AdapteeCollection { try { String provider = file.substring(0, file.length() - ".url".length()); InputStream providerFile = assetsManager.open(directory + "/" + file); - mainUrl = extractKeyFromInputStream(providerFile, MAIN_URL); - providerIp = extractKeyFromInputStream(providerFile, PROVIDER_IP); - providerApiIp = extractKeyFromInputStream(providerFile, PROVIDER_API_IP); - geoipUrl = extractKeyFromInputStream(providerFile, GEOIP_URL); - motdUrl = extractKeyFromInputStream(providerFile, MOTD_URL); + JSONObject providerConfig = inputStreamToJson(providerFile); + if (providerConfig != null) { + mainUrl = providerConfig.optString(MAIN_URL); + providerIp = providerConfig.optString(PROVIDER_IP); + providerApiIp = providerConfig.optString(PROVIDER_API_IP); + geoipUrl = providerConfig.optString(GEOIP_URL); + motdUrl = providerConfig.optString(MOTD_URL); + } certificate = loadInputStreamAsString(assetsManager.open(provider + EXT_PEM)); providerDefinition = loadInputStreamAsString(assetsManager.open(provider + EXT_JSON)); } catch (IOException e) { @@ -116,20 +122,20 @@ public class ProviderManager implements AdapteeCollection { private void addCustomProviders(File externalFilesDir) { this.externalFilesDir = externalFilesDir; customProviders = externalFilesDir != null && externalFilesDir.isDirectory() ? - providersFromFiles(externalFilesDir.list()) : + customProvidersFromFiles(externalFilesDir.list()) : new HashSet<>(); customProviderURLs = getProviderUrlSetFromProviderSet(customProviders); } - private Set providersFromFiles(String[] files) { + private Set customProvidersFromFiles(String[] files) { Set providers = new HashSet<>(); try { for (String file : files) { InputStream inputStream = getInputStreamFrom(externalFilesDir.getAbsolutePath() + "/" + file); - String mainUrl = extractKeyFromInputStream(inputStream, MAIN_URL); - String providerIp = extractKeyFromInputStream(inputStream, PROVIDER_IP); - String providerApiIp = extractKeyFromInputStream(inputStream, PROVIDER_API_IP); - providers.add(new Provider(mainUrl, providerIp, providerApiIp)); + JSONObject providerConfig = inputStreamToJson(inputStream); + String mainUrl = providerConfig.optString(MAIN_URL); + String domain = providerConfig.optString(DOMAIN); + providers.add(Provider.createCustomProvider(mainUrl, domain)); } } catch (FileNotFoundException | NullPointerException e) { e.printStackTrace(); @@ -238,7 +244,7 @@ public class ProviderManager implements AdapteeCollection { */ private void deleteLegacyCustomProviders() throws IOException, SecurityException { Set persistedCustomProviders = externalFilesDir != null && externalFilesDir.isDirectory() ? - providersFromFiles(externalFilesDir.list()) : new HashSet(); + customProvidersFromFiles(externalFilesDir.list()) : new HashSet(); persistedCustomProviders.removeAll(customProviders); for (Provider providerToDelete : persistedCustomProviders) { File providerFile = createFile(externalFilesDir, providerToDelete.getName() + EXT_JSON); diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderListBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderListBaseActivity.java index 90ebfb4d..eb9898b8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderListBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderListBaseActivity.java @@ -114,7 +114,7 @@ public abstract class ProviderListBaseActivity extends ProviderSetupBaseActivity } public void showAndSelectProvider(String newURL) { - provider = new Provider(newURL, null, null); + provider = new Provider(newURL); autoSelectProvider(); } 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 651aa345..d30e8b7e 100644 --- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java @@ -408,7 +408,6 @@ public class MockHelper { return getClass().getClassLoader().getResourceAsStream(filename); } }); - when(InputStreamHelper.extractKeyFromInputStream(any(InputStream.class), anyString())).thenCallRealMethod(); when(InputStreamHelper.inputStreamToJson(any(InputStream.class))).thenCallRealMethod(); } -- cgit v1.2.3 From c2c3e13455ecfa310711e78aa830d14804aaaa5c Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sun, 16 Apr 2023 00:23:02 +0200 Subject: only take experimental transports into consideration in case the user switched the experimental transport toggle --- .../se/leap/bitmaskclient/eip/GatewaySelector.java | 1 + .../se/leap/bitmaskclient/eip/GatewaysManager.java | 13 ++- .../leap/bitmaskclient/eip/VpnConfigGenerator.java | 5 ++ .../bitmaskclient/eip/GatewaysManagerTest.java | 93 ++++++++++++++++++++++ .../bitmaskclient/eip/VpnConfigGeneratorTest.java | 27 +++++++ .../resources/ptdemo_obfs4hop_tcp_gateways.json | 3 +- 6 files changed, 138 insertions(+), 4 deletions(-) (limited to 'app/src') diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java index 52030ce3..ad95c823 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java @@ -55,6 +55,7 @@ public class GatewaySelector { return offsets.isEmpty() ? null : offsets.firstEntry().getValue().iterator().next(); } + // calculateOffsets randomizes the order of Gateways with the same distance, e.g. from the same location private TreeMap> calculateOffsets() { TreeMap> offsets = new TreeMap>(); int localOffset = getCurrentTimezone(); diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java index 28766998..d114665b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -17,6 +17,7 @@ package se.leap.bitmaskclient.eip; import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_HOP; import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN; import static de.blinkt.openvpn.core.connection.Connection.TransportType.PT; import static se.leap.bitmaskclient.base.models.Constants.GATEWAYS; @@ -121,6 +122,8 @@ public class GatewaysManager { private ArrayList locations = new ArrayList<>(); private TransportType selectedTransport; + GatewaySelector gatewaySelector; + public GatewaysManager(Context context) { this.context = context; configureFromCurrentProvider(); @@ -147,7 +150,7 @@ public class GatewaysManager { } public GatewayOptions select(int nClosest, String city) { - TransportType[] transportTypes = getUseBridges(context) ? new TransportType[]{OBFS4} : new TransportType[]{OPENVPN}; + TransportType[] transportTypes = getUseBridges(context) ? new TransportType[]{OBFS4, OBFS4_HOP} : new TransportType[]{OPENVPN}; if (presortedList.size() > 0) { return getGatewayFromPresortedList(nClosest, transportTypes, city); } @@ -276,7 +279,9 @@ public class GatewaysManager { private GatewayOptions getGatewayFromTimezoneCalculation(int nClosest, TransportType[] transportTypes, @Nullable String city) { List list = new ArrayList<>(gateways.values()); - GatewaySelector gatewaySelector = new GatewaySelector(list); + if (gatewaySelector == null) { + gatewaySelector = new GatewaySelector(list); + } Gateway gateway; int found = 0; int i = 0; @@ -341,7 +346,9 @@ public class GatewaysManager { private int getPositionFromTimezoneCalculatedList(VpnProfile profile) { TransportType transportType = profile.getTransportType(); - GatewaySelector gatewaySelector = new GatewaySelector(new ArrayList<>(gateways.values())); + if (gatewaySelector == null) { + gatewaySelector = new GatewaySelector(new ArrayList<>(gateways.values())); + } Gateway gateway; int nClosest = 0; int i = 0; diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java index 90ffb6b0..2c22d4f7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java @@ -140,7 +140,12 @@ public class VpnConfigGenerator { } if (apiVersion >= 3) { for (TransportType transportType : transports.keySet()) { + Transport transport = transports.get(transportType); if (transportType.isPluggableTransport()) { + Transport.Options transportOptions = transport.getOptions(); + if (!experimentalTransports && transportOptions != null && transportOptions.isExperimental()) { + continue; + } try { profiles.put(transportType, createProfile(transportType)); } catch (ConfigParser.ConfigParseError | NumberFormatException | JSONException | IOException e) { diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java index 998ed2fd..ee6ccce5 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java @@ -16,6 +16,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import de.blinkt.openvpn.VpnProfile; @@ -214,6 +215,88 @@ public class GatewaysManagerTest { assertEquals("37.12.247.10", gatewaysManager.select(0).gateway.getRemoteIP()); } + @Test + public void TestSelectN_select_includeExperimentalTransport_DecoupledPortHoppingGW() { + Provider provider = getProvider(null, null, null, null, null, null, "decoupled_pt_portHopping.eip-service.json", null); + + MockHelper.mockProviderObservable(provider); + mockStatic(PreferenceHelper.class); + when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true); + when(PreferenceHelper.allowExperimentalTransports(any(Context.class))).thenReturn(true); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + ArrayList hosts = new ArrayList<>(); + hosts.add(gatewaysManager.select(0).gateway.getHost()); + hosts.add(gatewaysManager.select(1).gateway.getHost()); + + assertTrue(hosts.contains("bridge-nyc1-02.bitmask-dev.leapvpn.net")); + assertTrue(hosts.contains("bridge-nyc1-01.bitmask-dev.leapvpn.net")); + + } + + @Test + public void TestSelectN_select_includeExperimentalTransport_DecoupledPortAndIPHoppingGW() { + Provider provider = getProvider(null, null, null, null, null, null, "decoupled_pt.eip-service.json", null); + + MockHelper.mockProviderObservable(provider); + mockStatic(PreferenceHelper.class); + when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true); + when(PreferenceHelper.allowExperimentalTransports(any(Context.class))).thenReturn(true); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + + ArrayList hosts = new ArrayList<>(); + hosts.add(gatewaysManager.select(0).gateway.getHost()); + hosts.add(gatewaysManager.select(1).gateway.getHost()); + assertTrue(hosts.contains("bridge-nyc1-02.bitmask-dev.leapvpn.net")); + assertTrue(hosts.contains("bridge-nyc1-01.bitmask-dev.leapvpn.net")); + } + + @Test + public void TestSelectN_select_excludeExperimentalTransport_DecoupledPortHoppingGW() { + Provider provider = getProvider(null, null, null, null, null, null, "decoupled_pt_portHopping.eip-service.json", null); + + MockHelper.mockProviderObservable(provider); + mockStatic(PreferenceHelper.class); + when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true); + when(PreferenceHelper.allowExperimentalTransports(any(Context.class))).thenReturn(false); + + for (int i = 0; i < 1000; i++) { + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + assertEquals("bridge-nyc1-01.bitmask-dev.leapvpn.net", gatewaysManager.select(0).gateway.getHost()); + } + } + + @Test + public void TestSelectN_select_excludeExperimentalTransport_DecoupledPortAndIPHoppingGW() { + Provider provider = getProvider(null, null, null, null, null, null, "decoupled_pt.eip-service.json", null); + + MockHelper.mockProviderObservable(provider); + mockStatic(PreferenceHelper.class); + when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true); + when(PreferenceHelper.allowExperimentalTransports(any(Context.class))).thenReturn(false); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + + assertEquals("bridge-nyc1-01.bitmask-dev.leapvpn.net", gatewaysManager.select(0).gateway.getHost()); + assertNull(gatewaysManager.select(1)); + } + + @Test + public void TestSelectN_select_excludeExperimentalTransport_InGatewayHoppingPTBridge() { + Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_obfs4hop_tcp_gateways.json", null); + + MockHelper.mockProviderObservable(provider); + mockStatic(PreferenceHelper.class); + when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true); + when(PreferenceHelper.allowExperimentalTransports(any(Context.class))).thenReturn(false); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + + ArrayList hosts = new ArrayList<>(); + hosts.add(gatewaysManager.select(0).gateway.getHost()); + hosts.add(gatewaysManager.select(1).gateway.getHost()); + + assertTrue(hosts.contains("pt.demo.bitmask.net")); + assertTrue(hosts.contains("manila.bitmask.net")); + } + @Test public void testSelectN_selectFromPresortedGateways_returnsGatewaysInPresortedOrder() { Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", "ptdemo_three_mixed_gateways.geoip.json"); @@ -555,6 +638,16 @@ public class GatewaysManagerTest { Provider provider = getProvider(null, null, null, null, null, null, "decoupled_pt.eip-service.json", null); MockHelper.mockProviderObservable(provider); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + assertEquals(2, gatewaysManager.size()); + } + + @Test + public void testGatewayManagerFromCurrentProvider_decoupledBridgesIncludingExperimental_threeGateways() throws IOException, NullPointerException { + Provider provider = getProvider(null, null, null, null, null, null, "decoupled_pt.eip-service.json", null); + MockHelper.mockProviderObservable(provider); + mockStatic(PreferenceHelper.class); + when(PreferenceHelper.allowExperimentalTransports(any(Context.class))).thenReturn(true); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); assertEquals(3, gatewaysManager.size()); } diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java index 565e6396..93231055 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java @@ -2,6 +2,7 @@ package se.leap.bitmaskclient.eip; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; @@ -33,6 +34,7 @@ import java.io.File; import java.util.HashMap; import de.blinkt.openvpn.VpnProfile; +import de.blinkt.openvpn.core.ConfigParser; import de.blinkt.openvpn.core.connection.Connection; import de.blinkt.openvpn.core.connection.Obfs4Connection; import de.blinkt.openvpn.core.connection.Obfs4HopConnection; @@ -2041,6 +2043,7 @@ public class VpnConfigGeneratorTest { generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_obfs4hop_tcp_gateways.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; + configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); assertTrue(vpnProfiles.containsKey(OBFS4_HOP) && ((Obfs4HopConnection)vpnProfiles.get(OBFS4_HOP).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("tcp")); @@ -2053,6 +2056,7 @@ public class VpnConfigGeneratorTest { generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_obfs4hop_kcp_gateways.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; + configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); assertTrue(vpnProfiles.containsKey(OBFS4_HOP) && ((Obfs4HopConnection)vpnProfiles.get(OBFS4_HOP).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("kcp")); @@ -2066,6 +2070,7 @@ public class VpnConfigGeneratorTest { generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt_portHopping.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; + configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); System.out.println(vpnProfiles.get(OBFS4_HOP).getConfigFile(context, false)); @@ -2079,6 +2084,7 @@ public class VpnConfigGeneratorTest { generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt_portHopping.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; + configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); System.out.println(vpnProfiles.get(OBFS4_HOP).getConfigFile(context, false)); @@ -2091,6 +2097,7 @@ public class VpnConfigGeneratorTest { generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; + configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); assertTrue(vpnProfiles.containsKey(OBFS4)); @@ -2105,6 +2112,7 @@ public class VpnConfigGeneratorTest { generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; + configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); assertTrue(vpnProfiles.containsKey(OBFS4_HOP)); @@ -2113,6 +2121,24 @@ public class VpnConfigGeneratorTest { assertFalse(vpnProfiles.containsKey(OBFS4)); } + @Test + public void testGenerateVpnProfile_noExperimental_skipObfs4Hop() throws Exception { + when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt.eip-service.json"))).getJSONArray("gateways").getJSONObject(2); + generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); + VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); + configuration.apiVersion = 3; + configuration.experimentalTransports = false; + vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); + Exception exception = null; + try { + vpnConfigGenerator.generateVpnProfiles(); + } catch (ConfigParser.ConfigParseError e) { + exception = e; + } + assertNotNull(exception); + } + @Test public void testGenerateVpnProfile_obfs4hop_onlyPortHopping_decoupled() throws Exception { when(ConfigHelper.ObfsVpnHelper.useObfsVpn()).thenReturn(true); @@ -2120,6 +2146,7 @@ public class VpnConfigGeneratorTest { generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("decoupled_pt_portHopping.eip-service.json"))).getJSONObject(OPENVPN_CONFIGURATION); VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; + configuration.experimentalTransports = true; vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway, configuration); HashMap vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); assertTrue(vpnProfiles.containsKey(OBFS4_HOP)); diff --git a/app/src/test/resources/ptdemo_obfs4hop_tcp_gateways.json b/app/src/test/resources/ptdemo_obfs4hop_tcp_gateways.json index 6a748a4f..34bcecb3 100644 --- a/app/src/test/resources/ptdemo_obfs4hop_tcp_gateways.json +++ b/app/src/test/resources/ptdemo_obfs4hop_tcp_gateways.json @@ -66,7 +66,8 @@ { "type":"openvpn", "protocols":[ - "tcp" + "tcp", + "udp" ], "ports":[ "1195" -- cgit v1.2.3