diff options
author | cyBerta <cyberta@riseup.net> | 2024-06-17 02:52:06 +0200 |
---|---|---|
committer | cyBerta <cyberta@riseup.net> | 2024-06-17 02:52:06 +0200 |
commit | cd2e002c9e0f10079d8c1ec7af1d4be54a9de9e0 (patch) | |
tree | a9d514efbc47f2f47d8ac700996e4ef11d2f614c /app/src/main/java | |
parent | 3b3500e82fef981769cc6fff1dddc58cbe66ed48 (diff) |
update obfsvpn to version 1.0.0, this is a breaking API change. Obfsvpn requires openvpn in UDP as inner transport protocol from now on
Diffstat (limited to 'app/src/main/java')
21 files changed, 219 insertions, 574 deletions
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 ff27a5a2..6a5b016e 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java +++ b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java @@ -5,8 +5,6 @@ package de.blinkt.openvpn.core; -import static de.blinkt.openvpn.core.connection.Connection.TransportType.PT; - import android.os.Build; import android.text.TextUtils; @@ -27,9 +25,8 @@ 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; +import se.leap.bitmaskclient.pluggableTransports.models.Obfs4Options; //! Openvpn Config FIle Parser, probably not 100% accurate but close enough @@ -808,12 +805,10 @@ public class ConfigParser { } else { switch (transportType) { + case OBFS4_HOP: case OBFS4: conn = new Obfs4Connection(obfs4Options); break; - case OBFS4_HOP: - conn = new Obfs4HopConnection(obfs4Options); - break; case OPENVPN: conn = new OpenvpnConnection(); break; 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 9e778fae..c8ac965f 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -10,7 +10,6 @@ import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTED; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT; import static de.blinkt.openvpn.core.NetworkSpace.IpAddress; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PROFILE; -import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.useObfsVpn; import android.Manifest.permission; import android.app.Notification; @@ -53,9 +52,7 @@ 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.PtClientBuilder; -import se.leap.bitmaskclient.pluggableTransports.PtClientInterface; -import se.leap.bitmaskclient.pluggableTransports.ShapeshifterClient; +import se.leap.bitmaskclient.pluggableTransports.ObfsvpnClient; public class OpenVPNService extends VpnService implements StateListener, Callback, ByteCountListener, IOpenVPNServiceInternal, VpnNotificationManager.VpnServiceCallback { @@ -91,8 +88,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac private Toast mlastToast; private Runnable mOpenVPNThread; private VpnNotificationManager notificationManager; - private ShapeshifterClient shapeshifter; - private PtClientInterface obfsVpnClient; + private ObfsvpnClient obfsVpnClient; private FirewallManager firewallManager; private final IBinder mBinder = new IOpenVPNServiceInternal.Stub() { @@ -239,10 +235,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac if(isVpnRunning()) { if (getManagement() != null && getManagement().stopVPN(replaceConnection)) { if (!replaceConnection) { - if (shapeshifter != null) { - shapeshifter.stop(); - shapeshifter = null; - } else if (obfsVpnClient != null && obfsVpnClient.isStarted()) { + if (obfsVpnClient != null && obfsVpnClient.isStarted()) { obfsVpnClient.stop(); obfsVpnClient = null; } @@ -416,21 +409,15 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac mStarting = false; Connection.TransportType transportType = connection.getTransportType(); if (mProfile.usePluggableTransports() && transportType.isPluggableTransport()) { - if (useObfsVpn()) { - if (obfsVpnClient != null && obfsVpnClient.isStarted()) { - obfsVpnClient.stop(); - } - obfsVpnClient = PtClientBuilder.getPtClient(connection); - int runningSocksPort = obfsVpnClient.start(); - if (connection.getTransportType() == Connection.TransportType.OBFS4) { - connection.setProxyPort(String.valueOf(runningSocksPort)); - } - } else if (shapeshifter == null) { - shapeshifter = new ShapeshifterClient(((Obfs4Connection) connection).getObfs4Options()); - shapeshifter.start(); + if (obfsVpnClient != null && obfsVpnClient.isStarted()) { + obfsVpnClient.stop(); } + obfsVpnClient = new ObfsvpnClient(((Obfs4Connection) connection).getObfs4Options()); + obfsVpnClient.start(); + Log.d(TAG, "obfsvpn client started"); } + // Start a new session by creating a new thread. boolean useOpenVPN3 = VpnProfile.doUseOpenVPN3(this); @@ -484,12 +471,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac if (mOpenVPNThread != null) ((OpenVPNThread) mOpenVPNThread).setReplaceConnection(); if (mManagement.stopVPN(true)) { - // an old was asked to exit, wait 1s - if (shapeshifter != null) { - Log.d(TAG, "-> stop shapeshifter"); - shapeshifter.stop(); - shapeshifter = null; - } else if (obfsVpnClient != null && obfsVpnClient.isStarted()) { + if (obfsVpnClient != null && obfsVpnClient.isStarted()) { Log.d(TAG, "-> stop obfsvpnClient"); obfsVpnClient.stop(); obfsVpnClient = null; diff --git a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java index ecc03a19..73616ba4 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java +++ b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java @@ -223,7 +223,7 @@ public class VpnStatus { public enum ErrorType { UNKNOWN, - SHAPESHIFTER + OBFSVPN } // keytool -printcert -jarfile de.blinkt.openvpn_85.apk 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 19ea180d..c646c099 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4Connection.java +++ b/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4Connection.java @@ -1,12 +1,7 @@ package de.blinkt.openvpn.core.connection; -import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.useObfsVpn; -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; +import se.leap.bitmaskclient.pluggableTransports.ObfsvpnClient; +import se.leap.bitmaskclient.pluggableTransports.models.Obfs4Options; /** @@ -19,33 +14,10 @@ public class Obfs4Connection extends Connection { private Obfs4Options options; public Obfs4Connection(Obfs4Options options) { - if (useObfsVpn()) { - setServerName(options.gatewayIP); - setServerPort(options.transport.getPorts()[0]); - setProxyName(ObfsVpnClient.SOCKS_IP); - 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); - } + setServerName(ObfsvpnClient.IP); + setServerPort(String.valueOf(ObfsvpnClient.PORT)); + setProxyType(ProxyType.NONE); + setUseUdp(true); setProxyAuthUser(null); setProxyAuthPassword(null); setUseProxyAuth(false); @@ -61,7 +33,7 @@ public class Obfs4Connection extends Connection { @Override public TransportType getTransportType() { - return TransportType.OBFS4; + return options.transport.getTransportType(); } 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 deleted file mode 100644 index f983ae20..00000000 --- a/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4HopConnection.java +++ /dev/null @@ -1,48 +0,0 @@ -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/fragments/SettingsFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java index d7b62de2..33c8f388 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 @@ -8,7 +8,6 @@ import static se.leap.bitmaskclient.base.models.Constants.PREFER_UDP; import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES; import static se.leap.bitmaskclient.base.models.Constants.USE_IPv6_FIREWALL; import static se.leap.bitmaskclient.base.models.Constants.USE_OBFUSCATION_PINNING; -import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.useObfsVpn; import static se.leap.bitmaskclient.base.utils.ConfigHelper.isCalyxOSWithTetheringSupport; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.allowExperimentalTransports; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getExcludedApps; @@ -261,7 +260,7 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh public void initObfuscationPinningEntry(View rootView) { IconSwitchEntry obfuscationPinning = rootView.findViewById(R.id.obfuscation_proxy_pinning); - if (!BuildConfig.BUILD_TYPE.equals("debug") || !useObfsVpn()) { + if (!BuildConfig.BUILD_TYPE.equals("debug")) { obfuscationPinning.setVisibility(GONE); return; } @@ -302,7 +301,7 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh public void initExperimentalTransportsEntry(View rootView) { IconSwitchEntry experimentalTransports = rootView.findViewById(R.id.experimental_transports); - if (useObfsVpn() && ProviderObservable.getInstance().getCurrentProvider().supportsExperimentalPluggableTransports()) { + if (ProviderObservable.getInstance().getCurrentProvider().supportsExperimentalPluggableTransports()) { experimentalTransports.setVisibility(VISIBLE); experimentalTransports.setChecked(allowExperimentalTransports()); experimentalTransports.setOnCheckedChangeListener((buttonView, isChecked) -> { 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 cb9bd520..64e57cda 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 @@ -28,7 +28,6 @@ import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_ALLOWED_REGIS import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_ALLOW_ANONYMOUS; import static se.leap.bitmaskclient.base.models.Constants.TRANSPORT; import static se.leap.bitmaskclient.base.models.Constants.TYPE; -import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.useObfsVpn; import static se.leap.bitmaskclient.base.utils.RSAHelper.parseRsaKeyFromString; import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS; @@ -190,10 +189,7 @@ public final class Provider implements Parcelable { } public boolean supportsPluggableTransports() { - if (useObfsVpn()) { - 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)}); + return supportsTransports(new Pair[]{new Pair<>(OBFS4, TCP), new Pair<>(OBFS4, KCP), new Pair<>(OBFS4_HOP, TCP), new Pair<>(OBFS4_HOP, KCP)}); } public boolean supportsExperimentalPluggableTransports() { 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 e1f65b5e..22af1bfb 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 @@ -11,7 +11,6 @@ import se.leap.bitmaskclient.BuildConfig; public class BuildConfigHelper { public interface BuildConfigHelperInterface { - boolean useObfsVpn(); boolean hasObfuscationPinningDefaults(); String obfsvpnIP(); String obfsvpnPort(); @@ -21,10 +20,6 @@ public class BuildConfigHelper { } public static class DefaultBuildConfigHelper implements BuildConfigHelperInterface { - @Override - public boolean useObfsVpn() { - return BuildConfig.use_obfsvpn; - } @Override public boolean hasObfuscationPinningDefaults() { @@ -72,10 +67,6 @@ public class BuildConfigHelper { instance = helperInterface; } - public static boolean useObfsVpn() { - return instance.useObfsVpn(); - } - public static boolean hasObfuscationPinningDefaults() { return instance.hasObfuscationPinningDefaults(); } 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 eee3bfb2..8d1f21e5 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 @@ -513,8 +513,7 @@ public class PreferenceHelper { } public static boolean useObfuscationPinning() { - return BuildConfigHelper.useObfsVpn() && - getUseBridges() && + return getUseBridges() && getBoolean(USE_OBFUSCATION_PINNING, false) && !TextUtils.isEmpty(getObfuscationPinningIP()) && !TextUtils.isEmpty(getObfuscationPinningCert()) && diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java index adb0fdc7..bd626ce5 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java @@ -450,22 +450,6 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta if (BuildConfig.DEBUG) { Log.e("ERROR", logItem.getString(appContext)); } - switch (logItem.getErrorType()) { - case SHAPESHIFTER: - VpnProfile profile = VpnStatus.getLastConnectedVpnProfile(); - if (profile == null) { - EipCommand.startVPN(appContext, false, 0); - } else { - GatewaysManager gatewaysManager = new GatewaysManager(appContext); - int position = gatewaysManager.getPosition(profile); - setupNClosestGateway.set(position >= 0 ? position : 0); - selectNextGateway(); - } - break; - default: - break; - - } } } } 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 4c8fa797..a7a6bee8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java @@ -30,9 +30,6 @@ 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.UDP; -import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.useObfsVpn; -import static se.leap.bitmaskclient.pluggableTransports.ShapeshifterClient.DISPATCHER_IP; -import static se.leap.bitmaskclient.pluggableTransports.ShapeshifterClient.DISPATCHER_PORT; import androidx.annotation.VisibleForTesting; @@ -52,11 +49,12 @@ import de.blinkt.openvpn.core.ConfigParser; import de.blinkt.openvpn.core.VpnStatus; import de.blinkt.openvpn.core.connection.Connection; import de.blinkt.openvpn.core.connection.Connection.TransportType; +import de.blinkt.openvpn.core.connection.Obfs4Connection; 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; +import se.leap.bitmaskclient.pluggableTransports.ObfsvpnClient; +import se.leap.bitmaskclient.pluggableTransports.models.Obfs4Options; public class VpnConfigGenerator { private final JSONObject generalConfiguration; @@ -384,28 +382,12 @@ public class VpnConfigGenerator { } stringBuilder.append(getRouteString(ipAddress, transport)); - stringBuilder.append(getRemoteString(ipAddress, transport)); - stringBuilder.append(getExtraOptions(transport)); - } - - public String getRemoteString(String ipAddress, Transport transport) { - if (useObfsVpn()) { - if (useObfuscationPinning) { - return REMOTE + " " + obfuscationPinningIP + " " + obfuscationPinningPort + " 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; + String transparentProxyRemote = REMOTE + " " + ObfsvpnClient.IP + " " + ObfsvpnClient.PORT + " udp" + newLine; + stringBuilder.append(transparentProxyRemote); } + // TODO: figure out if any of these configs still make sense ( + @Deprecated public String getExtraOptions(Transport transport) { if (transport.getTransportType() == OBFS4_HOP) { return "replay-window 65535" + newLine + @@ -437,7 +419,7 @@ public class VpnConfigGenerator { return ""; } - // While openvpn in TCP mode is required for obfs4, openvpn in UDP mode is required for obfs4-hop + // With obfsvpn 1.0.0 openvpn is always required to run in UDP to work with any obfs4 based pluggable transport. 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 @@ -457,7 +439,7 @@ public class VpnConfigGenerator { return false; } - String requiredProtocol = transport.getTransportType() == OBFS4_HOP ? UDP : TCP; + String requiredProtocol = UDP; for (String protocol : protocols) { if (protocol.equals(requiredProtocol)) { return true; diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingObfsVpnClient.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingObfsVpnClient.java deleted file mode 100644 index 751208ba..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingObfsVpnClient.java +++ /dev/null @@ -1,66 +0,0 @@ -package se.leap.bitmaskclient.pluggableTransports; - -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]); - - HoppingConfig hoppingConfig = new HoppingConfig(kcp,IP+":"+PORT, options, 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/ObfsVpnClient.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java deleted file mode 100644 index 685349ed..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java +++ /dev/null @@ -1,144 +0,0 @@ -package se.leap.bitmaskclient.pluggableTransports; - -import static se.leap.bitmaskclient.base.models.Constants.KCP; - -import android.util.Log; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.Observable; -import java.util.Observer; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import client.Client; -import de.blinkt.openvpn.core.ConnectionStatus; -import de.blinkt.openvpn.core.VpnStatus; -import se.leap.bitmaskclient.eip.EipStatus; - -public class ObfsVpnClient implements PropertyChangeListener, PtClientInterface { - - public static final AtomicInteger SOCKS_PORT = new AtomicInteger(4430); - public static final String SOCKS_IP = "127.0.0.1"; - private static final String ERR_BIND = "bind: address already in use"; - - private static final String TAG = ObfsVpnClient.class.getSimpleName(); - private volatile boolean noNetwork; - private final AtomicBoolean pendingNetworkErrorHandling = new AtomicBoolean(false); - private final AtomicInteger reconnectRetry = new AtomicInteger(0); - private static final int MAX_RETRY = 5; - - private final client.Client_ obfsVpnClient; - private final Object LOCK = new Object(); - - public ObfsVpnClient(Obfs4Options options) 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()); - } - - /** - * starts the client - * @return the port ObfsVpn is running on - */ - public int start() { - synchronized (LOCK) { - obfsVpnClient.setEventLogger(this); - Log.d(TAG, "aquired LOCK"); - new Thread(this::startSync).start(); - waitUntilStarted(); - Log.d(TAG, "returning LOCK after " + (reconnectRetry.get() + 1) * 200 +" ms"); - } - return SOCKS_PORT.get(); - } - - // We're waiting here until the obfsvpn client has found a unbound port and started - private void waitUntilStarted() { - int count = -1; - try { - while (count < reconnectRetry.get() && reconnectRetry.get() < MAX_RETRY) { - Thread.sleep(200); - count++; - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - private void startSync() { - try { - obfsVpnClient.start(); - } catch (Exception e) { - Log.e(TAG, "[obfsvpn] exception: " + e.getLocalizedMessage()); - VpnStatus.logError("[obfsvpn] " + e.getLocalizedMessage()); - if (e.getLocalizedMessage() != null && e.getLocalizedMessage().contains(ERR_BIND) && reconnectRetry.get() < MAX_RETRY) { - reconnectRetry.addAndGet(1); - SOCKS_PORT.addAndGet(1); - obfsVpnClient.setSocksAddr(SOCKS_IP+":"+SOCKS_PORT.get()); - Log.d(TAG, "[obfsvpn] reconnecting on different port... " + SOCKS_PORT.get()); - VpnStatus.logDebug("[obfsvpn] reconnecting on different port... " + SOCKS_PORT.get()); - startSync(); - } else if (noNetwork) { - pendingNetworkErrorHandling.set(true); - } - } - } - - public void stop() { - synchronized (LOCK) { - Log.d(TAG, "stopping obfsVpnClient..."); - try { - obfsVpnClient.stop(); - reconnectRetry.set(0); - SOCKS_PORT.set(4430); - Thread.sleep(100); - } catch (Exception e) { - e.printStackTrace(); - VpnStatus.logError("[obfsvpn] " + e.getLocalizedMessage()); - } finally { - obfsVpnClient.setEventLogger(null); - } - pendingNetworkErrorHandling.set(false); - Log.d(TAG, "stopping obfsVpnClient releasing LOCK ..."); - } - } - - public boolean isStarted() { - return obfsVpnClient.isStarted(); - } - - // TODO: register observer! - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (EipStatus.PROPERTY_CHANGE.equals(evt.getPropertyName())) { - EipStatus status = (EipStatus) evt.getNewValue(); - if (status.getLevel() == ConnectionStatus.LEVEL_NONETWORK) { - noNetwork = true; - } else { - noNetwork = false; - if (pendingNetworkErrorHandling.getAndSet(false)) { - stop(); - start(); - } - } - } - } - - @Override - public void error(String s) { - VpnStatus.logError("[obfsvpn] " + s); - } - - @Override - public void log(String state, String message) { - VpnStatus.logDebug("[obfsvpn] " + state + " " + message); - } - -} diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsvpnClient.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsvpnClient.java new file mode 100644 index 00000000..dfdfbdd5 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsvpnClient.java @@ -0,0 +1,87 @@ +package se.leap.bitmaskclient.pluggableTransports; + +import android.util.Log; + +import client.Client; +import client.Client_; +import client.EventLogger; +import de.blinkt.openvpn.core.VpnStatus; +import de.blinkt.openvpn.core.connection.Connection; +import se.leap.bitmaskclient.base.models.Constants; +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; + +public class ObfsvpnClient { + + public static final int PORT = 8080; + public static final String IP = "127.0.0.1"; + private final Object LOCK = new Object(); + + + private static final String TAG = ObfsvpnClient.class.getSimpleName(); + + public final Client_ client; + + 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 kcpEnabled = Constants.KCP.equals(options.transport.getProtocols()[0]); + 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() ); + try { + Log.d(TAG, obfsvpnConfig.toString()); + client = Client.newFFIClient(obfsvpnConfig.toString()); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + public int start() { + synchronized (LOCK) { + new Thread(() -> { + try { + client.setEventLogger(new EventLogger() { + @Override + public void error(String s) { + VpnStatus.logError("[obfs4-client] " + s); + } + + @Override + public void log(String state, String message) { + VpnStatus.logDebug("[obfs4-client] " + state + ": " + message); + } + }); + client.start(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + return PORT; + } + } + + public void stop() { + synchronized (LOCK) { + try { + client.stop(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + client.setEventLogger(null); + } + } + } + + public boolean isStarted() { + return client.isStarted(); + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/PtClientBuilder.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/PtClientBuilder.java deleted file mode 100644 index 945e3d7a..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/PtClientBuilder.java +++ /dev/null @@ -1,18 +0,0 @@ -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 deleted file mode 100644 index 28d19a97..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/PtClientInterface.java +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index e57401f8..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ShapeshifterClient.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright (c) 2020 LEAP Encryption Access Project and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package se.leap.bitmaskclient.pluggableTransports; - -import android.os.Handler; -import android.os.Looper; -import android.util.Log; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -import de.blinkt.openvpn.core.ConnectionStatus; -import de.blinkt.openvpn.core.VpnStatus; -import se.leap.bitmaskclient.eip.EipStatus; - -public class ShapeshifterClient implements PropertyChangeListener { - - public static final String DISPATCHER_PORT = "4430"; - public static final String DISPATCHER_IP = "127.0.0.1"; - private static final int MAX_RETRY = 5; - private static final int RETRY_TIME = 4000; - private static final String TAG = ShapeshifterClient.class.getSimpleName(); - - private final shapeshifter.Shapeshifter_ shapeShifter; - private boolean isErrorHandling; - private boolean noNetwork; - private int retry = 0; - private final Handler reconnectHandler; - - @Deprecated - public class ShapeshifterLogger implements shapeshifter.Logger { - @Override - public void log(String s) { - Log.e(TAG, "SHAPESHIFTER ERROR: " + s); - VpnStatus.logError(s); - isErrorHandling = true; - close(); - - if (retry < MAX_RETRY && !noNetwork) { - retry++; - reconnectHandler.postDelayed(ShapeshifterClient.this::reconnect, RETRY_TIME); - } else { - VpnStatus.logError(VpnStatus.ErrorType.SHAPESHIFTER); - } - } - } - - public ShapeshifterClient(Obfs4Options options) { - shapeShifter = new shapeshifter.Shapeshifter_(); - shapeShifter.setLogger(new ShapeshifterLogger()); - setup(options); - Looper.prepare(); - reconnectHandler = new Handler(); - EipStatus.getInstance().addObserver(this); - Log.d(TAG, "shapeshifter initialized with: \n" + shapeShifter.toString()); - } - - private void setup(Obfs4Options options) { - shapeShifter.setSocksAddr(DISPATCHER_IP+":"+DISPATCHER_PORT); - shapeShifter.setTarget(options.gatewayIP +":"+options.transport.getPorts()[0]); - shapeShifter.setCert(options.transport.getOptions().getCert()); - } - - public void setOptions(Obfs4Options options) { - setup(options); - } - - public void start() { - try { - shapeShifter.open(); - } catch (Exception e) { - e.printStackTrace(); - Log.e(TAG, "SHAPESHIFTER ERROR: " + e.getLocalizedMessage()); - VpnStatus.logError(VpnStatus.ErrorType.SHAPESHIFTER); - VpnStatus.logError(e.getLocalizedMessage()); - } - } - - private void close() { - try { - shapeShifter.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private void reconnect() { - try { - shapeShifter.open(); - retry = 0; - isErrorHandling = false; - } catch (Exception e) { - e.printStackTrace(); - Log.e(TAG, "SHAPESHIFTER RECONNECTION ERROR: " + e.getLocalizedMessage()); - VpnStatus.logError("Unable to reconnect shapeshifter: " + e.getLocalizedMessage()); - } - } - - public boolean stop() { - try { - shapeShifter.close(); - return true; - } catch (Exception e) { - e.printStackTrace(); - VpnStatus.logError(e.getLocalizedMessage()); - } - EipStatus.getInstance().deleteObserver(this); - return false; - } - - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (EipStatus.PROPERTY_CHANGE.equals(evt.getPropertyName())) { - EipStatus status = (EipStatus) evt.getNewValue(); - if (status.getLevel() == ConnectionStatus.LEVEL_NONETWORK) { - noNetwork = true; - } else { - noNetwork = false; - if (isErrorHandling) { - isErrorHandling = false; - close(); - start(); - } - } - } - } -} diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingConfig.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/HoppingConfig.java index 3780b7dc..96b8c460 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/HoppingConfig.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/HoppingConfig.java @@ -1,4 +1,4 @@ -package se.leap.bitmaskclient.pluggableTransports; +package se.leap.bitmaskclient.pluggableTransports.models; import androidx.annotation.NonNull; @@ -9,35 +9,47 @@ import com.google.gson.GsonBuilder; import se.leap.bitmaskclient.base.models.Transport; public class HoppingConfig { - final boolean kcp; + + /** + * Enabled bool `json:"enabled"` + * Remotes []string `json:"remotes"` + * Obfs4Certs []string `json:"obfs4_certs"` + * PortSeed int64 `json:"port_seed"` + * PortCount uint `json:"port_count"` + * MinHopSeconds uint `json:"min_hop_seconds"` + * HopJitter uint `json:"hop_jitter"` + * } + */ + + final boolean enabled; final String proxyAddr; final String[] remotes; - final String[] certs; + final String[] obfs4Certs; final int portSeed; final int portCount; final int minHopSeconds; final int hopJitter; - public HoppingConfig(boolean kcp, + public HoppingConfig(boolean enabled, String proxyAddr, Obfs4Options options, int minHopSeconds, int hopJitter) { - this.kcp = kcp; + this.enabled = enabled; this.proxyAddr = proxyAddr; Transport transport = options.transport; Transport.Endpoint[] endpoints = transport.getOptions().getEndpoints(); 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() }; + this.remotes = new String[]{ options.bridgeIP }; + this.obfs4Certs = new String[] { transport.getOptions().getCert() }; } else { // port+ip hopping this.remotes = new String[endpoints.length]; - this.certs = new String[endpoints.length]; + this.obfs4Certs = new String[endpoints.length]; for (int i = 0; i < remotes.length; i++) { remotes[i] = endpoints[i].getIp(); - certs[i] = endpoints[i].getCert(); + obfs4Certs[i] = endpoints[i].getCert(); } } this.portSeed = transport.getOptions().getPortSeed(); diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/KcpConfig.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/KcpConfig.java new file mode 100644 index 00000000..255e7dd7 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/KcpConfig.java @@ -0,0 +1,39 @@ +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 KcpConfig { + + // check OpenVPN's --sndbuf size and --rcvbuf size + public static final int DEFAULT_KCP_SEND_WINDOW_SIZE = 32; + public static final int DEFAULT_KCP_RECEIVE_WINDOW_SIZE = 32; + public static final int DEFAULT_KCP_READ_BUFFER = 16 * 1024 * 1024; + public static final int DEFAULT_KCP_WRITE_BUFFER = 16 * 1024 * 1024; + + final boolean enabled; + final int sendWindowSize; + final int receiveWindowSize; + final int readBuffer; + final int writeBuffer; + + public KcpConfig(boolean enabled) { + this.enabled = enabled; + this.sendWindowSize = DEFAULT_KCP_SEND_WINDOW_SIZE; + this.receiveWindowSize = DEFAULT_KCP_RECEIVE_WINDOW_SIZE; + this.readBuffer = DEFAULT_KCP_READ_BUFFER; + this.writeBuffer = DEFAULT_KCP_WRITE_BUFFER; + } + + @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/Obfs4Options.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/Obfs4Options.java index 0dd81eb8..1eec376a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Obfs4Options.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/Obfs4Options.java @@ -1,16 +1,16 @@ -package se.leap.bitmaskclient.pluggableTransports; +package se.leap.bitmaskclient.pluggableTransports.models; import java.io.Serializable; import se.leap.bitmaskclient.base.models.Transport; public class Obfs4Options implements Serializable { - public String gatewayIP; + public String bridgeIP; public Transport transport; - public Obfs4Options(String gatewayIP, + public Obfs4Options(String bridgeIP, Transport transport) { - this.gatewayIP = gatewayIP; + this.bridgeIP = bridgeIP; this.transport = transport; } } 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 new file mode 100644 index 00000000..9f85c4a0 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/models/ObfsvpnConfig.java @@ -0,0 +1,35 @@ +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 ObfsvpnConfig { + + final String proxyAddr; + final HoppingConfig hoppingConfig; + final KcpConfig kcpConfig; + final String remoteIp; + final String remotePort; + final String obfs4Cert; + + public ObfsvpnConfig(String proxyAddress, HoppingConfig hoppingConfig, KcpConfig kcpConfig, String remoteIP, String remotePort, String obfsv4Cert) { + this.proxyAddr = proxyAddress; + this.hoppingConfig = hoppingConfig; + this.kcpConfig = kcpConfig; + this.remoteIp = remoteIP; + this.remotePort = remotePort; + this.obfs4Cert = obfsv4Cert; + } + + @NonNull + @Override + public String toString() { + Gson gson = new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .create(); + return gson.toJson(this); + } +} |