diff --git a/app/build.gradle b/app/build.gradle index 6df2616921d64d59c28f0073bc8bd0d2a7462c5e..b953df1f3c1f02bb4c949ecae9606d99fab47c0c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -66,7 +66,8 @@ android { buildConfigField "String", "obfsvpn_port", '"4431"' buildConfigField "String", "obfsvpn_ip", '"37.218.241.208"' buildConfigField "String", "obfsvpn_cert", '"k0L4LFg0Wk98v7P66xvgAx2ud+kggvjZX/qul3iFTJGH5X7xSHT+vVL4UZR0WI3SkmDzUg"' - buildConfigField 'boolean', 'obfsvpn_use_kcp', 'true' + // obfsvpn_transport_protocol can be any of "tcp", "kcp" or "quic" + buildConfigField "String", 'obfsvpn_transport_protocol', '"kcp"' // default to UDP usage buildConfigField 'boolean', 'prefer_udp', 'false' 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 9943faff8badc176f1b8729f9148e30054b0b422..6cd8610591e9697a2e523babfc5fc008d7f3b379 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 @@ -45,7 +45,8 @@ public abstract class Connection implements Serializable, Cloneable { public enum TransportProtocol { UDP("udp"), TCP("tcp"), - KCP("kcp"); + KCP("kcp"), + QUIC("quic"); final String protocol; diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/CensorshipCircumventionFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/CensorshipCircumventionFragment.java index e8789b32c7cda0526a3f4b60db700b037015138b..fc561d483f67bafcb15f8c20e397de14d5cc0c65 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/CensorshipCircumventionFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/CensorshipCircumventionFragment.java @@ -3,6 +3,7 @@ package se.leap.bitmaskclient.base.fragments; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseObfs4; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseObfs4Kcp; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUsePortHopping; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseObfs4Quic; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseSnowflake; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.hasSnowflakePrefs; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.resetSnowflakeSettings; @@ -37,6 +38,7 @@ public class CensorshipCircumventionFragment extends Fragment { public static int TUNNELING_AUTOMATICALLY = 100300000; public static int TUNNELING_OBFS4 = 100300001; public static int TUNNELING_OBFS4_KCP = 100300002; + public static int TUNNELING_QUIC = 100300003; private @NonNull FCensorshipCircumventionBinding binding; @@ -113,7 +115,7 @@ public class CensorshipCircumventionFragment extends Fragment { private void initTunneling() { RadioButton noneRadioButton = new RadioButton(binding.getRoot().getContext()); noneRadioButton.setText(getText(R.string.automatically_select)); - noneRadioButton.setChecked(!getUseObfs4() && !getUseObfs4Kcp()); + noneRadioButton.setChecked(!getUseObfs4() && !getUseObfs4Kcp() && !getUseObfs4Quic()); noneRadioButton.setId(TUNNELING_AUTOMATICALLY); binding.tunnelingRadioGroup.addView(noneRadioButton); @@ -133,6 +135,14 @@ public class CensorshipCircumventionFragment extends Fragment { binding.tunnelingRadioGroup.addView(obfs4KcpRadioButton); } + if (ProviderObservable.getInstance().getCurrentProvider().supportsObfs4Quic()) { + RadioButton obfs4QuicRadioButton = new RadioButton(binding.getRoot().getContext()); + obfs4QuicRadioButton.setText(getText(R.string.tunnelling_quic)); + obfs4QuicRadioButton.setId(TUNNELING_QUIC); + obfs4QuicRadioButton.setChecked(getUseObfs4Quic()); + binding.tunnelingRadioGroup.addView(obfs4QuicRadioButton); + } + binding.tunnelingRadioGroup.setOnCheckedChangeListener((group, checkedId) -> { useBridges(true); setUseTunnel(checkedId); diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java index 7d12ca70ec32cb9733fd57d94e243eadd6649275..2e4eec8aceffff617275468a4844d5d7a6940872 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java @@ -2,11 +2,16 @@ package se.leap.bitmaskclient.base.fragments; import static android.view.View.GONE; import static android.view.View.VISIBLE; +import static de.blinkt.openvpn.core.connection.Connection.TransportProtocol.KCP; +import static de.blinkt.openvpn.core.connection.Connection.TransportProtocol.QUIC; +import static de.blinkt.openvpn.core.connection.Connection.TransportProtocol.TCP; import android.app.Dialog; import android.os.Bundle; import android.text.TextUtils; import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -14,10 +19,10 @@ import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatDialogFragment; import androidx.appcompat.widget.AppCompatButton; import androidx.appcompat.widget.AppCompatEditText; +import androidx.appcompat.widget.AppCompatSpinner; import se.leap.bitmaskclient.base.utils.BuildConfigHelper; import se.leap.bitmaskclient.base.utils.PreferenceHelper; -import se.leap.bitmaskclient.base.views.IconSwitchEntry; import se.leap.bitmaskclient.databinding.DObfuscationProxyBinding; import se.leap.bitmaskclient.eip.GatewaysManager; @@ -30,7 +35,9 @@ public class ObfuscationProxyDialog extends AppCompatDialogFragment { AppCompatButton saveButton; AppCompatButton useDefaultsButton; AppCompatButton cancelButton; - IconSwitchEntry kcpSwitch; + AppCompatSpinner protocolSpinner; + private final String[] protocols = { TCP.toString(), KCP.toString(), QUIC.toString() }; + @NonNull @Override @@ -46,15 +53,29 @@ public class ObfuscationProxyDialog extends AppCompatDialogFragment { saveButton = binding.buttonSave; useDefaultsButton = binding.buttonDefaults; cancelButton = binding.buttonCancel; - kcpSwitch = binding.kcpSwitch; + protocolSpinner = binding.protocolSpinner; ipField.setText(PreferenceHelper.getObfuscationPinningIP()); portField.setText(PreferenceHelper.getObfuscationPinningPort()); certificateField.setText(PreferenceHelper.getObfuscationPinningCert()); - kcpSwitch.setChecked(PreferenceHelper.getObfuscationPinningKCP()); GatewaysManager gatewaysManager = new GatewaysManager(getContext()); + + ArrayAdapter<String> adapter = new ArrayAdapter<>(binding.getRoot().getContext(), android.R.layout.simple_spinner_item, protocols); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + protocolSpinner.setAdapter(adapter); + + protocolSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + PreferenceHelper.setObfuscationPinningProtocol(protocols[position]); + } + + @Override + public void onNothingSelected(AdapterView<?> parent) {} + }); + saveButton.setOnClickListener(v -> { String ip = TextUtils.isEmpty(ipField.getText()) ? null : ipField.getText().toString(); PreferenceHelper.setObfuscationPinningIP(ip); @@ -62,7 +83,6 @@ public class ObfuscationProxyDialog extends AppCompatDialogFragment { PreferenceHelper.setObfuscationPinningPort(port); String cert = TextUtils.isEmpty(certificateField.getText()) ? null : certificateField.getText().toString(); PreferenceHelper.setObfuscationPinningCert(cert); - PreferenceHelper.setObfuscationPinningKCP(kcpSwitch.isChecked()); PreferenceHelper.setUseObfuscationPinning(ip != null && port != null && cert != null); PreferenceHelper.setObfuscationPinningGatewayLocation(gatewaysManager.getLocationNameForIP(ip, v.getContext())); dismiss(); @@ -73,7 +93,7 @@ public class ObfuscationProxyDialog extends AppCompatDialogFragment { ipField.setText(BuildConfigHelper.obfsvpnIP()); portField.setText(BuildConfigHelper.obfsvpnPort()); certificateField.setText(BuildConfigHelper.obfsvpnCert()); - kcpSwitch.setChecked(BuildConfigHelper.useKcp()); + protocolSpinner.setSelection(getIndexForProtocol(BuildConfigHelper.obfsvpnTransportProtocol())); }); cancelButton.setOnClickListener(v -> { @@ -85,6 +105,15 @@ public class ObfuscationProxyDialog extends AppCompatDialogFragment { return builder.create(); } + private int getIndexForProtocol(String protocol) { + for (int i = 0; i < protocols.length; i++) { + if (protocols[i].equals(protocol)) { + return i; + } + } + return 0; + } + @Override public void onDestroyView() { super.onDestroyView(); diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java index 4567bf9744cd50fb389a5d9199f3316e34cf30e7..598f9908724db02b5fc1f7e1c0543d953da59ebd 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java @@ -15,6 +15,8 @@ import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getExcludedApps; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferUDP; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getShowAlwaysOnDialog; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseBridges; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseSnowflake; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.hasSnowflakePrefs; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.preferUDP; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.resetSnowflakeSettings; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setAllowExperimentalTransports; @@ -22,6 +24,7 @@ import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setUseObfuscatio import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setUsePortHopping; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setUseTunnel; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useBridges; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useManualDiscoverySettings; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useObfuscationPinning; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useSnowflake; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useManualBridgeSettings; @@ -132,14 +135,14 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh manualConfigRoot.setVisibility(ProviderObservable.getInstance().getCurrentProvider().supportsPluggableTransports() ? VISIBLE : GONE); IconTextEntry manualConfiguration = rootView.findViewById(R.id.bridge_manual_switch); SwitchCompat manualConfigurationSwitch = rootView.findViewById(R.id.bridge_manual_switch_control); - boolean usesManualBridge = useManualBridgeSettings(); - manualConfigurationSwitch.setChecked(usesManualBridge); + boolean useManualCircumventionSettings = useManualBridgeSettings() || useManualDiscoverySettings(); + manualConfigurationSwitch.setChecked(useManualCircumventionSettings); manualConfigurationSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { if (!buttonView.isPressed()) { return; } resetManualConfig(); - if (!usesManualBridge){ + if (!useManualCircumventionSettings){ openManualConfigurationFragment(); } }); 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 5a5d1d6e1a7ceacbc02042c5b02ffc0e47ae533a..b8849c4d182373f0791782cfbbbe4f12ae1e85d4 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 @@ -53,6 +53,7 @@ public interface Constants { String OBFUSCATION_PINNING_PORT = "obfuscation_pinning_port"; String OBFUSCATION_PINNING_CERT = "obfuscation_pinning_cert"; String OBFUSCATION_PINNING_KCP = "obfuscation_pinning_udp"; + String OBFUSCATION_PINNING_PROTOCOL = "obfuscation_pinning_protocol"; String OBFUSCATION_PINNING_LOCATION = "obfuscation_pinning_location"; String USE_SYSTEM_PROXY = "usesystemproxy"; String CUSTOM_PROVIDER_DOMAINS = "custom_provider_domains"; @@ -192,6 +193,7 @@ public interface Constants { String UDP = "udp"; String TCP = "tcp"; String KCP = "kcp"; + String QUIC = "quic"; String CAPABILITIES = "capabilities"; String TRANSPORT = "transport"; String TYPE = "type"; @@ -202,6 +204,10 @@ public interface Constants { String ENDPOINTS = "endpoints"; String PORT_SEED = "port_seed"; String PORT_COUNT = "port_count"; + String HOP_JITTER = "hop_jitter"; + String MIN_HOP_PORT = "min_hop_port"; + String MAX_HOP_PORT = "max_hop_port"; + String MIN_HOP_SECONDS = "min_hop_seconds"; String EXPERIMENTAL = "experimental"; String VERSION = "version"; String NAME = "name"; 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 89cea76b1d0dd0e8f497a6a56a438ea3a6682548..7679561645a853f8baf653f5b8767bd06015482f 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 @@ -17,6 +17,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.QUIC; 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; @@ -52,7 +53,6 @@ import java.security.PrivateKey; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; -import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.Set; @@ -303,11 +303,11 @@ public final class Provider implements Parcelable { } public boolean supportsPluggableTransports() { - 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), new Pair<>(OBFS4, KCP), new Pair<>(OBFS4, QUIC), new Pair<>(OBFS4_HOP, TCP), new Pair<>(OBFS4_HOP, KCP), new Pair<>(OBFS4_HOP, QUIC)}); } public boolean supportsExperimentalPluggableTransports() { - return supportsTransports(new Pair[]{new Pair<>(OBFS4, KCP), new Pair<>(OBFS4_HOP, TCP), new Pair<>(OBFS4_HOP, KCP)}); + return supportsTransports(new Pair[]{new Pair<>(OBFS4, KCP), new Pair<>(OBFS4_HOP, TCP), new Pair<>(OBFS4_HOP, KCP), new Pair<>(OBFS4, QUIC), new Pair<>(OBFS4_HOP, QUIC)}); } @@ -319,8 +319,12 @@ public final class Provider implements Parcelable { return supportsTransports(new Pair[]{new Pair<>(OBFS4, KCP)}); } + public boolean supportsObfs4Quic() { + return supportsTransports(new Pair[]{new Pair<>(OBFS4, QUIC)}); + } + public boolean supportsObfs4Hop() { - return supportsTransports(new Pair[]{new Pair<>(OBFS4_HOP, KCP),new Pair<>(OBFS4_HOP, TCP)}); + return supportsTransports(new Pair[]{new Pair<>(OBFS4_HOP, KCP), new Pair<>(OBFS4_HOP, QUIC), new Pair<>(OBFS4_HOP, TCP)}); } private boolean supportsTransports(Pair<TransportType, TransportProtocol>[] transportTypes) { @@ -996,7 +1000,7 @@ public final class Provider implements Parcelable { certificatePin = ""; certificatePinEncoding = ""; caCert = ""; - apiVersion = 3; + apiVersion = BuildConfig.preferred_client_api_version; privateKeyString = ""; vpnCertificate = ""; allowRegistered = false; 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 abd42812d1b7f8ea10a9a5b5983e398a85a12588..3b6956925343637c54015226293a1b763561e587 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,10 +1,17 @@ package se.leap.bitmaskclient.base.models; +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.CAPABILITIES; import static se.leap.bitmaskclient.base.models.Constants.CERT; +import static se.leap.bitmaskclient.base.models.Constants.HOP_JITTER; import static se.leap.bitmaskclient.base.models.Constants.IAT_MODE; +import static se.leap.bitmaskclient.base.models.Constants.MAX_HOP_PORT; +import static se.leap.bitmaskclient.base.models.Constants.MIN_HOP_PORT; +import static se.leap.bitmaskclient.base.models.Constants.MIN_HOP_SECONDS; import static se.leap.bitmaskclient.base.models.Constants.PORTS; +import static se.leap.bitmaskclient.base.models.Constants.PORT_COUNT; +import static se.leap.bitmaskclient.base.models.Constants.PORT_SEED; import static se.leap.bitmaskclient.base.models.Constants.PROTOCOLS; import static se.leap.bitmaskclient.base.models.Constants.TRANSPORT; @@ -21,6 +28,7 @@ import org.json.JSONObject; import java.io.Serializable; import java.util.Map; +import java.util.Objects; import java.util.Vector; import de.blinkt.openvpn.core.connection.Connection; @@ -94,6 +102,14 @@ public class Transport implements Serializable { } Map<String, Object> options = modelsBridge.getOptions(); Transport.Options transportOptions = new Transport.Options((String) options.get(CERT), (String) options.get(IAT_MODE)); + if (OBFS4_HOP.toString().equals(modelsBridge.getType())) { + transportOptions.minHopSeconds = getIntOption(options, MIN_HOP_SECONDS, 5); + transportOptions.minHopPort = getIntOption(options, MIN_HOP_PORT, 49152); + transportOptions.maxHopPort = getIntOption(options, MAX_HOP_PORT, 65535); + transportOptions.hopJitter = getIntOption(options, HOP_JITTER, 10); + transportOptions.portCount = getIntOption(options, PORT_COUNT, 100); + transportOptions.portSeed = getIntOption(options, PORT_SEED, 1); + } Transport transport = new Transport( modelsBridge.getType(), new String[]{modelsBridge.getTransport()}, @@ -103,6 +119,16 @@ public class Transport implements Serializable { return transport; } + private static int getIntOption(Map<String, Object> options, String key, int defaultValue) { + try { + Object o = options.get(key); + return (int) o; + } catch (NullPointerException | ClassCastException e){ + e.printStackTrace(); + return defaultValue; + } + } + public static Transport createTransportFrom(ModelsGateway modelsGateway) { if (modelsGateway == null) { return null; @@ -165,7 +191,6 @@ public class Transport implements Serializable { private final String cert; @SerializedName("iatMode") private final String iatMode; - @Nullable private Endpoint[] endpoints; @@ -174,23 +199,30 @@ public class Transport implements Serializable { private int portSeed; private int portCount; - + private int minHopPort; + private int maxHopPort; + private int minHopSeconds; + private int hopJitter; public Options(String cert, String iatMode) { this.cert = cert; this.iatMode = iatMode; } - 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, int portSeed, int portCount, int minHopPort, int maxHopPort, int minHopSeconds, int hopJitter, boolean experimental) { + this(iatMode, endpoints, null, portSeed, portCount, minHopPort, maxHopPort, minHopSeconds, hopJitter, experimental); } - public Options(String iatMode, Endpoint[] endpoints, String cert, int portSeed, int portCount, boolean experimental) { + public Options(String iatMode, Endpoint[] endpoints, String cert, int portSeed, int portCount, int minHopPort, int maxHopPort, int minHopSeconds, int hopJitter, boolean experimental) { this.iatMode = iatMode; this.endpoints = endpoints; this.portSeed = portSeed; this.portCount = portCount; this.experimental = experimental; + this.minHopPort = minHopPort; + this.maxHopPort = maxHopPort; + this.minHopSeconds = minHopSeconds; + this.hopJitter = hopJitter; this.cert = cert; } @@ -221,6 +253,22 @@ public class Transport implements Serializable { return portCount; } + public int getMinHopPort() { + return minHopPort; + } + + public int getMaxHopPort() { + return maxHopPort; + } + + public int getMinHopSeconds() { + return minHopSeconds; + } + + public int getHopJitter() { + return hopJitter; + } + @Override public String toString() { return new Gson().toJson(this); diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/BuildConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/BuildConfigHelper.java index 22af1bfb1ccbc83ceee7ac93395b957c85f4ece8..22939611199509e5fd0d606fb08bd8e7805561cb 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/BuildConfigHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/BuildConfigHelper.java @@ -15,7 +15,7 @@ public class BuildConfigHelper { String obfsvpnIP(); String obfsvpnPort(); String obfsvpnCert(); - boolean useKcp(); + String obfsvpnTransportProtocol(); boolean isDefaultBitmask(); } @@ -26,9 +26,11 @@ public class BuildConfigHelper { return BuildConfig.obfsvpn_ip != null && BuildConfig.obfsvpn_port != null && BuildConfig.obfsvpn_cert != null && + BuildConfig.obfsvpn_transport_protocol != null && !BuildConfig.obfsvpn_ip.isEmpty() && !BuildConfig.obfsvpn_port.isEmpty() && - !BuildConfig.obfsvpn_cert.isEmpty(); + !BuildConfig.obfsvpn_cert.isEmpty() && + !BuildConfig.obfsvpn_transport_protocol.isEmpty(); } @Override @@ -47,8 +49,8 @@ public class BuildConfigHelper { } @Override - public boolean useKcp() { - return BuildConfig.obfsvpn_use_kcp; + public String obfsvpnTransportProtocol() { + return BuildConfig.obfsvpn_transport_protocol; } @Override @@ -79,8 +81,8 @@ public class BuildConfigHelper { public static String obfsvpnCert() { return instance.obfsvpnCert(); } - public static boolean useKcp() { - return instance.useKcp(); + public static String obfsvpnTransportProtocol() { + return instance.obfsvpnTransportProtocol(); } public static boolean isDefaultBitmask() { diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java index b7c6db5d73991884e0d21bc0adf08e6adff21a13..ba644b91eb2212c885d69b4e6f399894f2d031c0 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java @@ -1,9 +1,11 @@ package se.leap.bitmaskclient.base.utils; import static android.content.Context.MODE_PRIVATE; +import static de.blinkt.openvpn.core.connection.Connection.TransportProtocol.TCP; import static se.leap.bitmaskclient.base.fragments.CensorshipCircumventionFragment.TUNNELING_AUTOMATICALLY; import static se.leap.bitmaskclient.base.fragments.CensorshipCircumventionFragment.TUNNELING_OBFS4; import static se.leap.bitmaskclient.base.fragments.CensorshipCircumventionFragment.TUNNELING_OBFS4_KCP; +import static se.leap.bitmaskclient.base.fragments.CensorshipCircumventionFragment.TUNNELING_QUIC; import static se.leap.bitmaskclient.base.models.Constants.ALLOW_EXPERIMENTAL_TRANSPORTS; import static se.leap.bitmaskclient.base.models.Constants.ALLOW_TETHERING_BLUETOOTH; import static se.leap.bitmaskclient.base.models.Constants.ALLOW_TETHERING_USB; @@ -23,9 +25,9 @@ import static se.leap.bitmaskclient.base.models.Constants.LAST_UPDATE_CHECK; import static se.leap.bitmaskclient.base.models.Constants.LAST_USED_PROFILE; import static se.leap.bitmaskclient.base.models.Constants.OBFUSCATION_PINNING_CERT; import static se.leap.bitmaskclient.base.models.Constants.OBFUSCATION_PINNING_IP; -import static se.leap.bitmaskclient.base.models.Constants.OBFUSCATION_PINNING_KCP; import static se.leap.bitmaskclient.base.models.Constants.OBFUSCATION_PINNING_LOCATION; import static se.leap.bitmaskclient.base.models.Constants.OBFUSCATION_PINNING_PORT; +import static se.leap.bitmaskclient.base.models.Constants.OBFUSCATION_PINNING_PROTOCOL; import static se.leap.bitmaskclient.base.models.Constants.PREFERENCES_APP_VERSION; import static se.leap.bitmaskclient.base.models.Constants.PREFERRED_CITY; import static se.leap.bitmaskclient.base.models.Constants.PREFER_UDP; @@ -587,12 +589,12 @@ public class PreferenceHelper { return getString(OBFUSCATION_PINNING_LOCATION, null); } - public static Boolean getObfuscationPinningKCP() { - return getBoolean(OBFUSCATION_PINNING_KCP, false); + public static String getObfuscationPinningProtocol() { + return getString(OBFUSCATION_PINNING_PROTOCOL, TCP.toString()); } - public static void setObfuscationPinningKCP(boolean isKCP) { - putBoolean(OBFUSCATION_PINNING_KCP, isKCP); + public static void setObfuscationPinningProtocol(String protocol) { + putString(OBFUSCATION_PINNING_PROTOCOL, protocol); } public static void setUseIPv6Firewall(boolean useFirewall) { @@ -615,8 +617,16 @@ public class PreferenceHelper { return getUseTunnel() == TUNNELING_OBFS4_KCP; } - public static boolean useManualBridgeSettings(){ - return (hasSnowflakePrefs() && getUseSnowflake()) || getUseObfs4() || getUseObfs4Kcp() || getUsePortHopping(); + public static boolean getUseObfs4Quic() { + return getUseTunnel() == TUNNELING_QUIC; + } + + public static boolean useManualBridgeSettings() { + return getUseObfs4() || getUseObfs4Kcp() || getUseObfs4Quic() || getUsePortHopping(); + } + + public static boolean useManualDiscoverySettings() { + return hasSnowflakePrefs() && getUseSnowflake(); } public static void setUseTunnel(int tunnel) { 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 d1ae7c43978137beb417a3d700b81684626c25dd..783f9124255cfdaeabb8ee30208c0221128459a2 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java @@ -29,15 +29,6 @@ import static se.leap.bitmaskclient.base.models.Constants.OVERLOAD; import static se.leap.bitmaskclient.base.models.Constants.TIMEZONE; import static se.leap.bitmaskclient.base.models.Constants.VERSION; import static se.leap.bitmaskclient.base.models.Transport.createTransportsFrom; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.allowExperimentalTransports; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getExcludedApps; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningCert; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningGatewayLocation; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningIP; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningKCP; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningPort; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferUDP; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useObfuscationPinning; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -294,7 +285,7 @@ public class Gateway { * In case the transport type is an obfuscation transport, you can pass a Vector of required transport layer protocols. * This way you can filter for TCP based obfs4 traffic versus KCP based obfs4 traffic. * @param transportType transport type, e.g. openvpn or obfs4 - * @param obfuscationTransportLayerProtocols filters for _any_ of these transport layer protocols (e.g. TCP or KCP) of a given obfuscation transportType, can be omitted if transportType is OPENVPN. + * @param obfuscationTransportLayerProtocols filters for _any_ of these transport layer protocols (e.g. TCP, KCP, QUIC) of a given obfuscation transportType, can be omitted if transportType is OPENVPN. * * @return */ 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 9655013c600a4fa5f3721e0b1ede34ef5ac09e85..16f12e024202bc1c3b121930b9f0bd99a3e8a621 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -26,16 +26,18 @@ import static se.leap.bitmaskclient.base.models.Constants.HOST; import static se.leap.bitmaskclient.base.models.Constants.IAT_MODE; import static se.leap.bitmaskclient.base.models.Constants.KCP; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.base.models.Constants.QUIC; import static se.leap.bitmaskclient.base.models.Constants.SORTED_GATEWAYS; import static se.leap.bitmaskclient.base.models.Constants.TCP; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningCert; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningIP; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningKCP; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningPort; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningProtocol; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferredCity; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseBridges; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseObfs4; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseObfs4Kcp; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseObfs4Quic; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUsePortHopping; import android.content.Context; @@ -174,7 +176,7 @@ public class GatewaysManager { if (getUsePortHopping()) { return new TransportType[]{OBFS4_HOP}; - } else if (getUseObfs4() || getUseObfs4Kcp()) { + } else if (getUseObfs4() || getUseObfs4Kcp() || getUseObfs4Quic()) { return new TransportType[]{OBFS4}; } else { return new TransportType[]{OBFS4, OBFS4_HOP}; @@ -192,10 +194,12 @@ public class GatewaysManager { return Set.of(TCP); } else if (getUseObfs4Kcp()) { return Set.of(KCP); + } else if (getUseObfs4Quic()) { + return Set.of(QUIC); } else { // If neither Obf4 nor Obf4Kcp are used, and bridges are enabled, - // then use both TCP and KCP (based on the original logic). - return Set.of(TCP, KCP); + // then allow to use any of these protocols + return Set.of(TCP, KCP, QUIC); } } @@ -432,7 +436,7 @@ public class GatewaysManager { try { Transport[] transports = new Transport[]{ new Transport(OBFS4.toString(), - new String[]{getObfuscationPinningKCP() ? "kcp" : "tcp"}, + new String[]{getObfuscationPinningProtocol()}, new String[]{getObfuscationPinningPort()}, getObfuscationPinningCert())}; GatewayJson.Capabilities capabilities = new GatewayJson.Capabilities(false, false, false, transports, false); @@ -492,7 +496,7 @@ public class GatewaysManager { options.put(CERT, getObfuscationPinningCert()); options.put(IAT_MODE, "0"); modelsBridge.options(options); - modelsBridge.transport(getObfuscationPinningKCP() ? "kcp" : "tcp"); + modelsBridge.transport(getObfuscationPinningProtocol()); modelsBridge.type(OBFS4.toString()); modelsBridge.host(PINNED_OBFUSCATION_PROXY); Gateway gateway = new Gateway(modelsEIPService, secrets, modelsBridge, provider.getApiVersion()); 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 f988dfa0174d63df4fcdf75863ecc9b7f24e69ce..0288ab2582468524695a62f073cad9548c36eefa 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java @@ -21,6 +21,7 @@ import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4_H import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN; import static se.leap.bitmaskclient.base.models.Constants.KCP; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.base.models.Constants.QUIC; 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.UDP; @@ -29,8 +30,8 @@ import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getExcludedApps; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningCert; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningGatewayLocation; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningIP; -import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningKCP; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningPort; +import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getObfuscationPinningProtocol; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferUDP; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useObfuscationPinning; @@ -69,7 +70,7 @@ public class VpnConfigGenerator { private final String obfuscationPinningIP; private final String obfuscationPinningPort; private final String obfuscationPinningCert; - private final boolean obfuscationPinningKCP; + private final String obfuscationPinningTransportProtocol; private final String remoteGatewayIP; private final String remoteGatewayIPv6; private final String profileName; @@ -89,7 +90,7 @@ public class VpnConfigGenerator { Set<String> excludedApps = null; boolean useObfuscationPinning; - boolean obfuscationProxyKCP; + String obfuscationProxyTransportProtocol = ""; String obfuscationProxyIP = ""; String obfuscationProxyPort = ""; String obfuscationProxyCert = ""; @@ -110,7 +111,7 @@ public class VpnConfigGenerator { config.obfuscationProxyIP = getObfuscationPinningIP(); config.obfuscationProxyPort = getObfuscationPinningPort(); config.obfuscationProxyCert = getObfuscationPinningCert(); - config.obfuscationProxyKCP = getObfuscationPinningKCP(); + config.obfuscationProxyTransportProtocol = getObfuscationPinningProtocol(); } config.transports = transports; return config; @@ -128,7 +129,7 @@ public class VpnConfigGenerator { this.obfuscationPinningIP = config.obfuscationProxyIP; this.obfuscationPinningPort = config.obfuscationProxyPort; this.obfuscationPinningCert = config.obfuscationProxyCert; - this.obfuscationPinningKCP = config.obfuscationProxyKCP; + this.obfuscationPinningTransportProtocol = config.obfuscationProxyTransportProtocol; this.remoteGatewayIP = config.remoteGatewayIP; this.remoteGatewayIPv6 = config.remoteGatewayIPv6; this.transports = config.transports; @@ -196,7 +197,7 @@ public class VpnConfigGenerator { String ip = remoteGatewayIP; if (useObfuscationPinning) { transport = new Transport(OBFS4.toString(), - new String[]{obfuscationPinningKCP ? KCP : TCP}, + new String[]{obfuscationPinningTransportProtocol}, new String[]{obfuscationPinningPort}, obfuscationPinningCert); ip = obfuscationPinningIP; @@ -326,7 +327,7 @@ public class VpnConfigGenerator { 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) || QUIC.equals(protocol); } return false; } 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 6ee49951daa8658e47cac09978d707a1a4e100af..625bbfd84926c97bb8c6dff2e7ee9615ed9a8ab8 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,8 @@ package se.leap.bitmaskclient.pluggableTransports; +import static se.leap.bitmaskclient.base.models.Constants.KCP; +import static se.leap.bitmaskclient.base.models.Constants.QUIC; + import android.util.Log; import client.Client; @@ -12,6 +15,7 @@ import se.leap.bitmaskclient.pluggableTransports.models.HoppingConfig; import se.leap.bitmaskclient.pluggableTransports.models.KcpConfig; import se.leap.bitmaskclient.pluggableTransports.models.Obfs4Options; import se.leap.bitmaskclient.pluggableTransports.models.ObfsvpnConfig; +import se.leap.bitmaskclient.pluggableTransports.models.QuicConfig; public class ObfsvpnClient implements EventLogger { @@ -29,14 +33,17 @@ public class ObfsvpnClient implements EventLogger { //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 kcpEnabled = Constants.KCP.equals(options.transport.getProtocols()[0]); + String protocol = options.transport.getProtocols()[0]; + boolean kcpEnabled = KCP.equals(protocol); + boolean quicEnabled = QUIC.equals(protocol); boolean hoppingEnabled = options.transport.getTransportType() == Connection.TransportType.OBFS4_HOP; if (!hoppingEnabled && (options.transport.getPorts() == null || options.transport.getPorts().length == 0)) { throw new IllegalStateException("obf4 based transport has no bridge ports configured"); } KcpConfig kcpConfig = new KcpConfig(kcpEnabled); - HoppingConfig hoppingConfig = new HoppingConfig(hoppingEnabled,IP+":"+PORT, options, 10, 10); - ObfsvpnConfig obfsvpnConfig = new ObfsvpnConfig(IP+":"+PORT, hoppingConfig, kcpConfig, options.bridgeIP, options.transport.getPorts()[0], options.transport.getOptions().getCert() ); + QuicConfig quicConfig = new QuicConfig(quicEnabled); + HoppingConfig hoppingConfig = new HoppingConfig(hoppingEnabled,IP+":"+PORT, options); + ObfsvpnConfig obfsvpnConfig = new ObfsvpnConfig(IP+":"+PORT, hoppingConfig, kcpConfig, quicConfig, options.bridgeIP, options.transport.getPorts()[0], options.transport.getOptions().getCert() ); try { Log.d(TAG, obfsvpnConfig.toString()); client = Client.newFFIClient(obfsvpnConfig.toString()); diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/HoppingConfig.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/HoppingConfig.java index 96b8c4606ff17a20fb8f620325b07dde50c1cbae..0dc2d5085f076bd18f1998b56039f1b2944061af 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/HoppingConfig.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/HoppingConfig.java @@ -11,14 +11,15 @@ import se.leap.bitmaskclient.base.models.Transport; public class HoppingConfig { /** - * Enabled bool `json:"enabled"` + * Enabled bool `json:"enabled"` * Remotes []string `json:"remotes"` * Obfs4Certs []string `json:"obfs4_certs"` * PortSeed int64 `json:"port_seed"` * PortCount uint `json:"port_count"` + * MinHopPort uint `json:"min_hop_port"` + * MaxHopPort uint `json:"max_hop_port"` * MinHopSeconds uint `json:"min_hop_seconds"` * HopJitter uint `json:"hop_jitter"` - * } */ final boolean enabled; @@ -29,12 +30,12 @@ public class HoppingConfig { final int portCount; final int minHopSeconds; final int hopJitter; + final int minHopPort; + final int maxHopPort; public HoppingConfig(boolean enabled, String proxyAddr, - Obfs4Options options, - int minHopSeconds, - int hopJitter) { + Obfs4Options options) { this.enabled = enabled; this.proxyAddr = proxyAddr; Transport transport = options.transport; @@ -54,8 +55,10 @@ public class HoppingConfig { } this.portSeed = transport.getOptions().getPortSeed(); this.portCount = transport.getOptions().getPortCount(); - this.minHopSeconds = minHopSeconds; - this.hopJitter = hopJitter; + this.minHopSeconds = transport.getOptions().getMinHopSeconds(); + this.hopJitter = transport.getOptions().getHopJitter(); + this.minHopPort = transport.getOptions().getMinHopPort(); + this.maxHopPort = transport.getOptions().getMaxHopPort(); } @NonNull diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/ObfsvpnConfig.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/ObfsvpnConfig.java index 9f85c4a04807e5dc247f4e6b53a8e9b7fa5adbce..cfcd6b6c3b6449c5c35cea0885fd3ee968ea28d7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/ObfsvpnConfig.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/ObfsvpnConfig.java @@ -11,14 +11,16 @@ public class ObfsvpnConfig { final String proxyAddr; final HoppingConfig hoppingConfig; final KcpConfig kcpConfig; + final QuicConfig quicConfig; final String remoteIp; final String remotePort; final String obfs4Cert; - public ObfsvpnConfig(String proxyAddress, HoppingConfig hoppingConfig, KcpConfig kcpConfig, String remoteIP, String remotePort, String obfsv4Cert) { + public ObfsvpnConfig(String proxyAddress, HoppingConfig hoppingConfig, KcpConfig kcpConfig, QuicConfig quicConfig, String remoteIP, String remotePort, String obfsv4Cert) { this.proxyAddr = proxyAddress; this.hoppingConfig = hoppingConfig; this.kcpConfig = kcpConfig; + this.quicConfig = quicConfig; this.remoteIp = remoteIP; this.remotePort = remotePort; this.obfs4Cert = obfsv4Cert; diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/QuicConfig.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/QuicConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..dd377dd74591e193df9e02a64bb6d018d3f0d40e --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/QuicConfig.java @@ -0,0 +1,24 @@ +package se.leap.bitmaskclient.pluggableTransports.models; + +import androidx.annotation.NonNull; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class QuicConfig { + final boolean enabled; + + public QuicConfig(boolean enabled) { + this.enabled = enabled; + } + + @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/res/layout/d_obfuscation_proxy.xml b/app/src/main/res/layout/d_obfuscation_proxy.xml index 7b8fcaa734897d54cb0816a64b72689b634f7919..03ffb61ffc6a90b8832c4140964011539caa632b 100644 --- a/app/src/main/res/layout/d_obfuscation_proxy.xml +++ b/app/src/main/res/layout/d_obfuscation_proxy.xml @@ -57,16 +57,32 @@ android:id="@+id/cert_field" android:layout_width="match_parent" android:layout_height="wrap_content"/> - <se.leap.bitmaskclient.base.views.IconSwitchEntry - android:id="@+id/kcp_switch" + + <TextView android:layout_width="match_parent" android:layout_height="wrap_content" - app:text="KCP" - app:subtitle="UDP based network protocol" - app:icon="@drawable/ic_multiple_stop" - > - - </se.leap.bitmaskclient.base.views.IconSwitchEntry> + android:text="Network protocol" + android:textStyle="bold" + android:paddingTop="@dimen/activity_margin" + android:textAppearance="@android:style/TextAppearance.DeviceDefault" /> + <androidx.appcompat.widget.LinearLayoutCompat + android:id="@+id/protocol_spinner_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:gravity="center_vertical" + android:orientation="horizontal"> + <androidx.appcompat.widget.AppCompatImageView + android:layout_width="?android:attr/listPreferredItemHeightSmall" + android:layout_height="?android:attr/listPreferredItemHeightSmall" + android:src="@drawable/ic_multiple_stop" + android:padding="@dimen/stdpadding"/> + <androidx.appcompat.widget.AppCompatSpinner + android:id="@+id/protocol_spinner" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:tooltipText="select nework protocol"/> + </androidx.appcompat.widget.LinearLayoutCompat> <androidx.appcompat.widget.LinearLayoutCompat android:layout_width="wrap_content" diff --git a/app/src/main/res/values/untranslatable.xml b/app/src/main/res/values/untranslatable.xml index bd8fa0e77d56eeb33b1276083d7192e1cee99bde..3d499e85b605637d944d6e79f845f8293820ab7c 100644 --- a/app/src/main/res/values/untranslatable.xml +++ b/app/src/main/res/values/untranslatable.xml @@ -76,4 +76,5 @@ </string-array> <string name="tunnelling_obfs4" translatable="false">Obfs4</string> <string name="tunnelling_obfs4_kcp" translatable="false">Obfs4+KCP</string> + <string name="tunnelling_quic" translatable="false">Quic</string> </resources> diff --git a/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java b/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java index 43df146b05789038619e168e7a7ab00695b86766..aa098b94928b04360f76e034d57515d36e37e872 100644 --- a/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java +++ b/app/src/test/java/de/blinkt/openvpn/VpnProfileTest.java @@ -33,15 +33,16 @@ import se.leap.bitmaskclient.pluggableTransports.models.Obfs4Options; 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,\"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\":\"8080\",\"mUseUdp\":true,\"mProxyPort\":\"\",\"mUseCustomConfig\":false,\"options\":{\"bridgeIP\":\"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\":\"127.0.0.1\",\"mProxyType\":\"NONE\",\"mConnectTimeout\":0,\"mServerPort\":\"8080\",\"mUseUdp\":true,\"mProxyPort\":\"\",\"mUseCustomConfig\":false,\"options\":{\"bridgeIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"portCount\":0,\"iatMode\":\"1\",\"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_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\":\"127.0.0.1\",\"mProxyType\":\"NONE\",\"mConnectTimeout\":0,\"mServerPort\":\"8080\",\"mUseUdp\":true,\"mProxyPort\":\"\",\"mUseCustomConfig\":false,\"options\":{\"bridgeIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"portCount\":0,\"iatMode\":\"1\",\"cert\":\"CERT\",\"experimental\":false,\"portSeed\":0},\"type\":\"obfs4\",\"protocols\":[\"kcp\"],\"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}\n"; + 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\":\"8080\",\"mUseUdp\":true,\"mProxyPort\":\"\",\"mUseCustomConfig\":false,\"options\":{\"bridgeIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"minHopSeconds\":0,\"portCount\":0,\"iatMode\":\"0\",\"cert\":\"CERT\",\"experimental\":false,\"minHopPort\":0,\"portSeed\":0,\"hopJitter\":0,\"maxHopPort\":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\":\"127.0.0.1\",\"mProxyType\":\"NONE\",\"mConnectTimeout\":0,\"mServerPort\":\"8080\",\"mUseUdp\":true,\"mProxyPort\":\"\",\"mUseCustomConfig\":false,\"options\":{\"bridgeIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"minHopSeconds\":0,\"portCount\":0,\"iatMode\":\"1\",\"cert\":\"CERT\",\"experimental\":false,\"minHopPort\":0,\"portSeed\":0,\"hopJitter\":0,\"maxHopPort\":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_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\":\"127.0.0.1\",\"mProxyType\":\"NONE\",\"mConnectTimeout\":0,\"mServerPort\":\"8080\",\"mUseUdp\":true,\"mProxyPort\":\"\",\"mUseCustomConfig\":false,\"options\":{\"bridgeIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"minHopSeconds\":0,\"portCount\":0,\"iatMode\":\"1\",\"cert\":\"CERT\",\"experimental\":false,\"minHopPort\":0,\"portSeed\":0,\"hopJitter\":0,\"maxHopPort\":0},\"type\":\"obfs4\",\"protocols\":[\"kcp\"],\"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}\n"; + private static final String OBFS4CONNECTION_PROFILE_OBFSVPN_QUIC = "{\"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\":\"8080\",\"mUseUdp\":true,\"mProxyPort\":\"\",\"mUseCustomConfig\":false,\"options\":{\"bridgeIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"minHopSeconds\":0,\"portCount\":0,\"iatMode\":\"1\",\"cert\":\"CERT\",\"experimental\":false,\"minHopPort\":0,\"portSeed\":0,\"hopJitter\":0,\"maxHopPort\":0},\"type\":\"obfs4\",\"protocols\":[\"quic\"],\"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}\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\":\"127.0.0.1\",\"mProxyType\":\"NONE\",\"mConnectTimeout\":0,\"mServerPort\":\"8080\",\"mUseUdp\":true,\"mProxyPort\":\"\",\"mUseCustomConfig\":false,\"options\":{\"bridgeIP\":\"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\":\"\",\"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 = "{\"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\":\"8080\",\"mUseUdp\":true,\"mProxyPort\":\"\",\"mUseCustomConfig\":false,\"options\":{\"bridgeIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"minHopSeconds\":10,\"portCount\":100,\"endpoints\":[{\"ip\":\"1.1.1.1\",\"cert\":\"CERT1\"},{\"ip\":\"2.2.2.2\",\"cert\":\"CERT2\"}],\"iatMode\":\"1\",\"experimental\":true,\"minHopPort\":40000,\"portSeed\":2500,\"hopJitter\":10,\"maxHopPort\":64000},\"type\":\"obfs4\",\"protocols\":[\"tcp\"],\"ports\":[\"1234\"]}},\"mProxyName\":\"\",\"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\":\"127.0.0.1\",\"mProxyType\":\"NONE\",\"mConnectTimeout\":0,\"mServerPort\":\"8080\",\"mUseUdp\":true,\"mProxyPort\":\"\",\"mUseCustomConfig\":false,\"options\":{\"bridgeIP\":\"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\":\"\",\"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\":\"127.0.0.1\",\"mProxyType\":\"NONE\",\"mConnectTimeout\":0,\"mServerPort\":\"8080\",\"mUseUdp\":true,\"mProxyPort\":\"\",\"mUseCustomConfig\":false,\"options\":{\"bridgeIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"minHopSeconds\":10,\"portCount\":100,\"endpoints\":[{\"ip\":\"1.1.1.1\",\"cert\":\"CERT1\"},{\"ip\":\"2.2.2.2\",\"cert\":\"CERT2\"}],\"iatMode\":\"1\",\"experimental\":true,\"minHopPort\":40000,\"portSeed\":2500,\"hopJitter\":10,\"maxHopPort\":64000},\"type\":\"obfs4\",\"protocols\":[\"kcp\"],\"ports\":[\"1234\"]}},\"mProxyName\":\"\",\"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\":\"127.0.0.1\",\"mProxyType\":\"NONE\",\"mConnectTimeout\":0,\"mServerPort\":\"8080\",\"mUseUdp\":true,\"mProxyPort\":\"\",\"mUseCustomConfig\":false,\"options\":{\"bridgeIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"portCount\":100,\"iatMode\":\"1\",\"cert\":\"CERT\",\"experimental\":true,\"portSeed\":200},\"type\":\"obfs4-hop\",\"protocols\":[\"tcp\"],\"ports\":[\"1234\"]}},\"mProxyName\":\"\",\"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\":\"127.0.0.1\",\"mProxyType\":\"NONE\",\"mConnectTimeout\":0,\"mServerPort\":\"8080\",\"mUseUdp\":true,\"mProxyPort\":\"\",\"mUseCustomConfig\":false,\"options\":{\"bridgeIP\":\"192.168.0.1\",\"transport\":{\"options\":{\"minHopSeconds\":10,\"portCount\":100,\"iatMode\":\"1\",\"cert\":\"CERT\",\"experimental\":true,\"minHopPort\":40000,\"portSeed\":2500,\"hopJitter\":10,\"maxHopPort\":64000},\"type\":\"obfs4-hop\",\"protocols\":[\"tcp\"],\"ports\":[\"1234\"]}},\"mProxyName\":\"\",\"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() { @@ -128,12 +129,30 @@ public class VpnProfileTest { assertEquals(expectation.toString(),actual.toString()); } + @Test + public void toJson_obfs4_obfsvpn_quic() throws JSONException { + + VpnProfile mockVpnProfile = new VpnProfile("mockProfile", OBFS4); + Transport.Options options = new Transport.Options("CERT", "1"); + Transport transport = new Transport(OBFS4.toString(), new String[]{"quic"}, new String[]{"1234"}, options); + mockVpnProfile.mConnections[0] = new Obfs4Connection(new Obfs4Options("192.168.0.1", transport)); + mockVpnProfile.mLastUsed = 0; + String s = mockVpnProfile.toJson(); + + //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_QUIC); + + assertEquals(expectation.toString(),actual.toString()); + } + @Test public void toJson_obfs4hop_kcp() throws JSONException { 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.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, 40000, 64000, 10, 10, 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)); @@ -153,7 +172,7 @@ public class VpnProfileTest { VpnProfile mockVpnProfile = new VpnProfile("mockProfile", OBFS4_HOP); - Transport.Options options = new Transport.Options("1", null, "CERT",200, 100, true); + Transport.Options options = new Transport.Options("1", null, "CERT",2500, 100, 40000, 64000, 10, 10, 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)); @@ -172,7 +191,7 @@ public class VpnProfileTest { public void toJson_obfs4hop() throws JSONException { 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.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, 40000, 64000, 10, 10, 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)); @@ -242,4 +261,21 @@ public class VpnProfileTest { assertEquals("192.168.0.1", obfs4Connection.getObfs4Options().bridgeIP); assertEquals("1234", obfs4Connection.getObfs4Options().transport.getPorts()[0]); } + + @Test + public void fromJson_obfs4_obfsvpn_quic() { + + VpnProfile mockVpnProfile = VpnProfile.fromJson(OBFS4CONNECTION_PROFILE_OBFSVPN_QUIC); + assertNotNull(mockVpnProfile); + assertNotNull(mockVpnProfile.mConnections); + assertNotNull(mockVpnProfile.mConnections[0]); + assertTrue(mockVpnProfile.mConnections[0].isUseUdp()); + Obfs4Connection obfs4Connection = (Obfs4Connection) mockVpnProfile.mConnections[0]; + assertEquals(OBFS4, obfs4Connection.getTransportType()); + assertEquals("quic", 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().bridgeIP); + 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 801f98ad8198894f986f284c634dd532d65ca23a..1e07b293aa6c7f1c16871098f7abf8c5fc884620 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\",\"experimental\":false,\"portSeed\":0,\"portCount\":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,\"minHopPort\":0,\"maxHopPort\":0,\"minHopSeconds\":0,\"hopJitter\":0}}],\"user_ips\":false}}"; Connection.TransportType transportType = OBFS4; Transport[] transports = new Transport[]{ diff --git a/app/src/test/java/se/leap/bitmaskclient/base/models/ProviderTest.java b/app/src/test/java/se/leap/bitmaskclient/base/models/ProviderTest.java index dc35ecb9fd3dc9b5d77bab251f4e7305fb4b9e15..fc2e6d0325bfa97d20a66bed7b57e11f93105ab0 100644 --- a/app/src/test/java/se/leap/bitmaskclient/base/models/ProviderTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/base/models/ProviderTest.java @@ -124,6 +124,20 @@ public class ProviderTest { assertTrue(p1.supportsExperimentalPluggableTransports()); } + @Test + public void testIsExperimentalPluggableTransportsSupported_Obfs4Quic_returnsTrue() throws Exception { + Provider p1 = TestSetupHelper.getProvider( + "https://pt.demo.bitmask.net", + null, + null, + null, + null, + null, + "multiple_pts_per_host_eip-service.json", + null); + assertTrue(p1.supportsExperimentalPluggableTransports()); + } + @Test public void testSupportsPluggableTransports_Obfs4Kcp_obsvpn_returnsTrue() throws Exception { BuildConfigHelper helper = MockHelper.mockBuildConfigHelper(true); @@ -140,4 +154,20 @@ public class ProviderTest { assertTrue(p1.supportsPluggableTransports()); } + @Test + public void testSupportsPluggableTransports_Obfs4Quic_obsvpn_returnsTrue() throws Exception { + BuildConfigHelper helper = MockHelper.mockBuildConfigHelper(true); + + Provider p1 = TestSetupHelper.getProvider( + "https://pt.demo.bitmask.net", + null, + null, + null, + null, + null, + "ptdemo_only_experimental_transports_gateways.json", + null); + assertTrue(p1.supportsPluggableTransports()); + } + } diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/GatewayTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/GatewayTest.java index 88e58cd8698a29e1e454ce35fbd879ea716cdbdf..fc0620793ab2af66f1b6ea7c6cf50b82d8953824 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/GatewayTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/GatewayTest.java @@ -1,11 +1,12 @@ package se.leap.bitmaskclient.eip; -import static org.junit.Assert.*; - +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static se.leap.bitmaskclient.base.models.Constants.GATEWAYS; -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.Provider.CA_CERT; import static se.leap.bitmaskclient.testutils.TestSetupHelper.getProvider; import android.content.Context; @@ -33,11 +34,8 @@ import de.blinkt.openvpn.core.ConfigParser; import de.blinkt.openvpn.core.connection.Connection; import de.blinkt.openvpn.core.connection.Obfs4Connection; import se.leap.bitmaskclient.base.models.Provider; -import se.leap.bitmaskclient.base.models.ProviderObservable; import se.leap.bitmaskclient.base.utils.PreferenceHelper; -import se.leap.bitmaskclient.base.utils.TimezoneHelper; import se.leap.bitmaskclient.testutils.MockSharedPreferences; -import se.leap.bitmaskclient.testutils.TestSetupHelper; @RunWith(RobolectricTestRunner.class) @Config(sdk = {Build.VERSION_CODES.P}) @@ -129,6 +127,7 @@ public class GatewayTest { assertTrue(gateway.supportsTransport(Connection.TransportType.OBFS4, null)); assertTrue(gateway.supportsTransport(Connection.TransportType.OBFS4, new HashSet<>(Arrays.asList("kcp")))); assertTrue(gateway.supportsTransport(Connection.TransportType.OBFS4, new HashSet<>(Arrays.asList("tcp")))); + assertTrue(gateway.supportsTransport(Connection.TransportType.OBFS4, new HashSet<>(Arrays.asList("quic")))); assertFalse(gateway.supportsTransport(Connection.TransportType.OBFS4, new HashSet<>(Arrays.asList("invalid")))); } 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 327e208580e4ac209b20d4756d5e31610609b189..fa5d888b659b6272e45ee60f90c778f42b0251d9 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java @@ -1653,7 +1653,7 @@ public class VpnConfigGeneratorTest { configuration.obfuscationProxyPort = "443"; configuration.obfuscationProxyIP = "5.6.7.8"; configuration.obfuscationProxyCert = "asdfasdf"; - configuration.obfuscationProxyKCP = true; + configuration.obfuscationProxyTransportProtocol = "kcp"; configuration.remoteGatewayIP = "1.2.3.4"; configuration.transports = createTransportsFrom(gateway, 3); vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, configuration); @@ -1671,7 +1671,7 @@ public class VpnConfigGeneratorTest { VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; configuration.useObfuscationPinning = true; - configuration.obfuscationProxyKCP = false; + configuration.obfuscationProxyTransportProtocol = "tcp"; configuration.obfuscationProxyPort = "443"; configuration.obfuscationProxyIP = "5.6.7.8"; configuration.obfuscationProxyCert = "asdfasdf"; @@ -1692,7 +1692,7 @@ public class VpnConfigGeneratorTest { VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); configuration.apiVersion = 3; configuration.useObfuscationPinning = true; - configuration.obfuscationProxyKCP = true; + configuration.obfuscationProxyTransportProtocol = "kcp"; configuration.obfuscationProxyPort = "443"; configuration.obfuscationProxyIP = "5.6.7.8"; configuration.obfuscationProxyCert = "asdfasdf"; @@ -1706,6 +1706,28 @@ public class VpnConfigGeneratorTest { assertTrue("bridge is pinned one", getVpnProfile(vpnProfiles, OBFS4).getTransportType() == OBFS4 && getVpnProfile(vpnProfiles, OBFS4).mGatewayIp.equals("1.2.3.4")); assertTrue("bridge is running KCP", ((Obfs4Connection) getVpnProfile(vpnProfiles, OBFS4).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("kcp")); } + + @Test + public void testGenerateVpnProfile_ObfuscationPinningEnabled_quic_obfs4QuicProfile () throws Exception { + gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_kcp_gateways.json"))).getJSONArray("gateways").getJSONObject(0); + generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("ptdemo_kcp_gateways.json"))).getJSONObject(OPENVPN_CONFIGURATION); + VpnConfigGenerator.Configuration configuration = new VpnConfigGenerator.Configuration(); + configuration.apiVersion = 3; + configuration.useObfuscationPinning = true; + configuration.obfuscationProxyTransportProtocol = "quic"; + configuration.obfuscationProxyPort = "443"; + configuration.obfuscationProxyIP = "5.6.7.8"; + configuration.obfuscationProxyCert = "asdfasdf"; + configuration.remoteGatewayIP = "1.2.3.4"; + configuration.transports = createTransportsFrom(gateway, 3); + + vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, configuration); + Vector<VpnProfile> vpnProfiles = vpnConfigGenerator.generateVpnProfiles(); + assertFalse("has openvpn profile", containsKey(vpnProfiles, OPENVPN)); + assertTrue("has no obfs4 profile", containsKey(vpnProfiles, OBFS4)); + assertTrue("bridge is pinned one", getVpnProfile(vpnProfiles, OBFS4).getTransportType() == OBFS4 && getVpnProfile(vpnProfiles, OBFS4).mGatewayIp.equals("1.2.3.4")); + assertTrue("bridge is running QUIC", ((Obfs4Connection) getVpnProfile(vpnProfiles, OBFS4).mConnections[0]).getObfs4Options().transport.getProtocols()[0].equals("quic")); + } @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); 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 c89d08cbea66cecbab9d239232b136f1b4e22934..cb22603399866aa9237c4b575359363f4de44427 100644 --- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java @@ -203,8 +203,8 @@ public class MockHelper { } @Override - public boolean useKcp() { - return false; + public String obfsvpnTransportProtocol() { + return "tcp"; } @Override diff --git a/app/src/test/resources/multiple_pts_per_host_eip-service.json b/app/src/test/resources/multiple_pts_per_host_eip-service.json index 2bd053d8e8f0df1106652b3a2059c010bb2b47ce..c2fec3568d2369f7d10525a370ddf4bf0e05855b 100644 --- a/app/src/test/resources/multiple_pts_per_host_eip-service.json +++ b/app/src/test/resources/multiple_pts_per_host_eip-service.json @@ -41,6 +41,19 @@ "kcp" ], "type":"obfs4" + }, + { + "options":{ + "cert":"XXXX", + "iatMode":"0" + }, + "ports":[ + "4432" + ], + "protocols":[ + "quic" + ], + "type":"obfs4" } ] },