diff options
Diffstat (limited to 'app/src/main/java')
29 files changed, 346 insertions, 623 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 506b04a6..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() { @@ -187,7 +183,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac mProcessThread = null; } VpnStatus.removeByteCountListener(this); - unregisterDeviceStateReceiver(); + unregisterDeviceStateReceiver(mDeviceStateReceiver); + mDeviceStateReceiver = null; mOpenVPNThread = null; if (!mStarting) { stopForeground(!mNotificationAlwaysVisible); @@ -200,35 +197,31 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac firewallManager.stop(); } - synchronized void registerDeviceStateReceiver(OpenVPNManagement magnagement) { + synchronized void registerDeviceStateReceiver(DeviceStateReceiver newDeviceStateReceiver) { // Registers BroadcastReceiver to track network connection changes. IntentFilter filter = new IntentFilter(); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); - mDeviceStateReceiver = new DeviceStateReceiver(magnagement); // Fetch initial network state - mDeviceStateReceiver.networkStateChange(this); - - registerReceiver(mDeviceStateReceiver, filter); - VpnStatus.addByteCountListener(mDeviceStateReceiver); + newDeviceStateReceiver.networkStateChange(this); + registerReceiver(newDeviceStateReceiver, filter); + VpnStatus.addByteCountListener(newDeviceStateReceiver); } - synchronized void unregisterDeviceStateReceiver() { + synchronized void unregisterDeviceStateReceiver(DeviceStateReceiver deviceStateReceiver) { if (mDeviceStateReceiver != null) try { - VpnStatus.removeByteCountListener(mDeviceStateReceiver); - this.unregisterReceiver(mDeviceStateReceiver); + VpnStatus.removeByteCountListener(deviceStateReceiver); + this.unregisterReceiver(deviceStateReceiver); } catch (IllegalArgumentException iae) { // I don't know why this happens: // java.lang.IllegalArgumentException: Receiver not registered: de.blinkt.openvpn.NetworkSateReceiver@41a61a10 // Ignore for now ... iae.printStackTrace(); } - mDeviceStateReceiver = null; - } @Override @@ -242,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; } @@ -419,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); @@ -469,14 +453,16 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac mProcessThread.start(); } - new Handler(getMainLooper()).post(() -> { - if (mDeviceStateReceiver != null) { - unregisterDeviceStateReceiver(); - } - registerDeviceStateReceiver(mManagement); - } + final DeviceStateReceiver oldDeviceStateReceiver = mDeviceStateReceiver; + final DeviceStateReceiver newDeviceStateReceiver = new DeviceStateReceiver(mManagement); - ); + guiHandler.post(() -> { + if (oldDeviceStateReceiver != null) + unregisterDeviceStateReceiver(oldDeviceStateReceiver); + + registerDeviceStateReceiver(newDeviceStateReceiver); + mDeviceStateReceiver = newDeviceStateReceiver; + }); } private void stopOldOpenVPNProcess() { @@ -485,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; @@ -538,6 +519,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac @Override public void onCreate() { super.onCreate(); + guiHandler = new Handler(getMainLooper()); notificationManager = new VpnNotificationManager(this); firewallManager = new FirewallManager(this, true); } 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..0fe6bff2 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,12 @@ 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)); + setUseUdp(true); + setProxyType(ProxyType.NONE); + setProxyName(""); + setProxyPort(""); setProxyAuthUser(null); setProxyAuthPassword(null); setUseProxyAuth(false); @@ -61,7 +35,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/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java index 21fc81e4..5363f16e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java @@ -51,6 +51,7 @@ import se.leap.bitmaskclient.base.models.ProviderObservable; import se.leap.bitmaskclient.base.utils.DateHelper; import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.eip.EipCommand; +import se.leap.bitmaskclient.providersetup.ProviderSetupObservable; import se.leap.bitmaskclient.providersetup.activities.SetupActivity; /** @@ -178,9 +179,10 @@ public class StartActivity extends Activity{ if ((hasNewFeature(FeatureVersionCode.CALYX_PROVIDER_LILYPAD_UPDATE) && ( getPackageName().equals("org.calyxinstitute.vpn") || ProviderObservable.getInstance().getCurrentProvider().getDomain().equals("calyx.net"))) || - hasNewFeature(FeatureVersionCode.RISEUP_PROVIDER_LILYPAD_UPDATE) && ( + (hasNewFeature(FeatureVersionCode.RISEUP_PROVIDER_LILYPAD_UPDATE) || hasNewFeature(FeatureVersionCode.RISEUP_PROVIDER_LILYPAD_UPDATE_v2) + && ( getPackageName().equals("se.leap.riseupvpn") || - ProviderObservable.getInstance().getCurrentProvider().getDomain().equals("riseup.net"))) { + ProviderObservable.getInstance().getCurrentProvider().getDomain().equals("riseup.net")))) { // deletion of current configured provider so that a new provider setup is triggered Provider provider = ProviderObservable.getInstance().getCurrentProvider(); if (provider != null && !provider.isDefault()) { @@ -267,6 +269,7 @@ public class StartActivity extends Activity{ } private void showNextActivity(Provider provider) { + ProviderSetupObservable.cancel(); if (provider.shouldShowMotdSeen()) { try { IMessages messages = Motd.newMessages(provider.getMotdJsonString()); 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..51bce6d4 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; @@ -225,7 +224,7 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh private void initGatewayPinningEntry(View rootView) { IconTextEntry gatewayPinning = rootView.findViewById(R.id.gateway_pinning); - if (!BuildConfig.BUILD_TYPE.equals("debug")) { + if (!BuildConfig.DEBUG_MODE) { gatewayPinning.setVisibility(GONE); return; } @@ -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.DEBUG_MODE) { 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/FeatureVersionCode.java b/app/src/main/java/se/leap/bitmaskclient/base/models/FeatureVersionCode.java index 8b78c966..1771f0b0 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/models/FeatureVersionCode.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/models/FeatureVersionCode.java @@ -5,6 +5,7 @@ public interface FeatureVersionCode { int GEOIP_SERVICE = 148; int CALYX_PROVIDER_LILYPAD_UPDATE = 165000; int RISEUP_PROVIDER_LILYPAD_UPDATE = 165000; + int RISEUP_PROVIDER_LILYPAD_UPDATE_v2 = 172000; int ENCRYPTED_SHARED_PREFS = 170000; int NOTIFICATION_PREMISSION_API_UPDATE = 170000; 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/models/Transport.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Transport.java index 33fbbf7a..c2590012 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 @@ -14,18 +14,18 @@ import java.io.Serializable; import de.blinkt.openvpn.core.connection.Connection; public class Transport implements Serializable { - private String type; - private String[] protocols; + private final String type; + private final String[] protocols; @Nullable - private String[] ports; + private final String[] ports; @Nullable - private Options options; + private final Options options; public Transport(String type, String[] protocols, String[] ports, String cert) { this(type, protocols, ports, new Options(cert, "0")); } - public Transport(String type, String[] protocols, String[] ports, Options options) { + public Transport(String type, String[] protocols, @Nullable String[] ports, @Nullable Options options) { this.type = type; this.protocols = protocols; this.ports = ports; @@ -69,9 +69,9 @@ public class Transport implements Serializable { public static class Options implements Serializable { @Nullable - private String cert; + private final String cert; @SerializedName("iatMode") - private String iatMode; + private final String iatMode; @Nullable private Endpoint[] endpoints; @@ -136,8 +136,8 @@ public class Transport implements Serializable { public static class Endpoint implements Serializable { - private String ip; - private String cert; + private final String ip; + private final String cert; public Endpoint(String ip, String cert) { this.ip = ip; 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/FileHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java index f1d86876..9d29911b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java @@ -70,7 +70,7 @@ public class FileHelper { } reader.close(); return sb.toString(); - } catch (IOException errabi) { + } catch (NullPointerException | IOException errabi) { return null; } } 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 ed95b75c..bd626ce5 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java @@ -81,7 +81,6 @@ import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.providersetup.ProviderAPI; import se.leap.bitmaskclient.providersetup.ProviderAPICommand; import se.leap.bitmaskclient.providersetup.ProviderSetupObservable; -import se.leap.bitmaskclient.providersetup.activities.SetupActivity; import se.leap.bitmaskclient.tor.TorServiceCommand; import se.leap.bitmaskclient.tor.TorStatusObservable; @@ -245,10 +244,8 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta Log.d(TAG, "PROVIDER OK - FETCH SUCCESSFUL"); //no break, continue with next case case CORRECTLY_DOWNLOADED_VPN_CERTIFICATE: - if (ProviderSetupObservable.getProgress() > 0 && !activityForeground.get()) { - Intent activityIntent = new Intent(appContext, SetupActivity.class); - activityIntent.setAction(Intent.ACTION_MAIN); - appContext.startActivity(activityIntent); + if (ProviderSetupObservable.isSetupRunning() && !activityForeground.get()) { + ProviderSetupObservable.storeLastResult(resultCode, resultData); } break; case TOR_TIMEOUT: @@ -270,6 +267,8 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta for (EipSetupListener listener : listeners) { listener.handleProviderApiEvent(intent); } + + } private void maybeStartEipService(Bundle resultData) { @@ -451,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..8cbc4289 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,14 +439,14 @@ 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; } } - VpnStatus.logError("Misconfigured provider: " + transport.getTransportType().toString() + " currently only allows openvpn in " + requiredProtocol + " mode! Skipping config for ip " + ipAddress); + VpnStatus.logError("Provider - client incompatibility: obfuscation protocol " + transport.getTransportType().toString() + " currently only allows OpenVPN in " + requiredProtocol + " mode! Skipping config for ip " + ipAddress); return false; } 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..bf030a0f --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsvpnClient.java @@ -0,0 +1,89 @@ +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 implements EventLogger { + + 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()); + client.setEventLogger(this); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + public int start() { + + synchronized (LOCK) { + new Thread(() -> { + try { + client.start(); + } catch (Exception e) { + e.printStackTrace(); + } + }).start(); + 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(); + } + + @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); + } +} 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); + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupObservable.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupObservable.java index c882b0bb..237f5bd2 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupObservable.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupObservable.java @@ -16,6 +16,8 @@ package se.leap.bitmaskclient.providersetup; * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +import android.os.Bundle; + import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; @@ -45,13 +47,15 @@ public class ProviderSetupObservable { public static final String PROPERTY_CHANGE = "ProviderSetupObservable"; private final HandlerInterface handler; private long lastUpdate = 0; + private int resultCode = 0; + private Bundle resultData; private ProviderSetupObservable() { handler = HandlerProvider.get(); changeSupport = new PropertyChangeSupport(this); - + resultData = new Bundle(); } public void addObserver(PropertyChangeListener propertyChangeListener) { @@ -70,6 +74,14 @@ public class ProviderSetupObservable { return instance; } + public static void storeLastResult(int resultCode, Bundle resultData) { + if (getInstance().canceled) { + return; + } + getInstance().resultCode = resultCode; + getInstance().resultData = resultData; + } + public static void updateProgress(int progress) { if (getInstance().canceled) { return; @@ -105,8 +117,14 @@ public class ProviderSetupObservable { return getInstance().progress; } + public static boolean isSetupRunning() { + return getInstance().progress > 0; + } + public static void reset() { getInstance().progress = 0; + getInstance().resultCode = 0; + getInstance().resultData = new Bundle(); getInstance().changeSupport.firePropertyChange(PROPERTY_CHANGE, null, getInstance()); } @@ -121,5 +139,16 @@ public class ProviderSetupObservable { public static void startSetup() { getInstance().canceled = false; + getInstance().resultCode = 0; + getInstance().progress = 1; + getInstance().resultData = new Bundle(); + } + + public static int getResultCode() { + return getInstance().resultCode; + } + + public static Bundle getResultData() { + return getInstance().resultData; } } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java index 9235daad..9a0bf7b7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java @@ -3,6 +3,7 @@ package se.leap.bitmaskclient.providersetup.activities; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static androidx.appcompat.app.ActionBar.DISPLAY_SHOW_CUSTOM; +import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.isDefaultBitmask; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.deleteProviderDetailsFromPreferences; import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.CONFIGURE_PROVIDER_FRAGMENT; @@ -139,7 +140,7 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal }); binding.viewPager.setAdapter(adapter); binding.viewPager.setUserInputEnabled(false); - binding.viewPager.setCurrentItem(currentPosition, false); + binding.setupNextButton.setOnClickListener(v -> { int currentPos = binding.viewPager.getCurrentItem(); @@ -153,6 +154,14 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal cancel(); }); setupActionBar(); + + if (ProviderSetupObservable.isSetupRunning()) { + provider = ProviderSetupObservable.getResultData().getParcelable(PROVIDER_KEY); + if (provider != null) { + currentPosition = adapter.getFragmentPostion(CONFIGURE_PROVIDER_FRAGMENT); + } + } + binding.viewPager.setCurrentItem(currentPosition, false); } @Override diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java index cdb255fc..621cb41a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java @@ -132,8 +132,12 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Prop binding.progressSpinner.update(ProviderSetupObservable.getProgress()); setupActivityCallback.setNavigationButtonHidden(true); setupActivityCallback.setCancelButtonHidden(false); - ProviderSetupObservable.startSetup(); - ProviderAPICommand.execute(getContext(), SET_UP_PROVIDER, setupActivityCallback.getSelectedProvider()); + if (ProviderSetupObservable.isSetupRunning()) { + handleResult(ProviderSetupObservable.getResultCode(), ProviderSetupObservable.getResultData(), true); + } else { + ProviderSetupObservable.startSetup(); + ProviderAPICommand.execute(getContext(), SET_UP_PROVIDER, setupActivityCallback.getSelectedProvider()); + } } protected void showConnectionDetails() { @@ -203,16 +207,21 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Prop if (resultData == null) { resultData = Bundle.EMPTY; } + handleResult(resultCode, resultData, false); + } + + private void handleResult(int resultCode, Bundle resultData, boolean resumeSetup) { Provider provider = resultData.getParcelable(PROVIDER_KEY); if (ignoreProviderAPIUpdates || provider == null || (setupActivityCallback.getSelectedProvider() != null && - !setupActivityCallback.getSelectedProvider().getMainUrlString().equals(provider.getMainUrlString()))) { + !setupActivityCallback.getSelectedProvider().getMainUrlString().equals(provider.getMainUrlString()))) { return; } switch (resultCode) { case PROVIDER_OK: + setupActivityCallback.onProviderSelected(provider); if (provider.allowsAnonymous()) { ProviderAPICommand.execute(this.getContext(), DOWNLOAD_VPN_CERTIFICATE, provider); } else { @@ -223,11 +232,13 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Prop setupActivityCallback.onProviderSelected(provider); handler.postDelayed(() -> { if (!ProviderSetupObservable.isCanceled()) { - if (setupActivityCallback != null) { + try { setupActivityCallback.onConfigurationSuccess(); + } catch (NullPointerException npe) { + // callback disappeared in the meanwhile } } - }, 750); + }, resumeSetup ? 0 : 750); break; case PROVIDER_NOK: case INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE: diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java b/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java index f13eb70e..b7909865 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java @@ -1,6 +1,6 @@ package se.leap.bitmaskclient.tor; /** - * Copyright (c) 2021 LEAP Encryption Access Project and contributors + * Copyright (c) 2024 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 @@ -66,6 +66,7 @@ public class ClientTransportPlugin implements ClientTransportPluginInterface, Pr this.contextRef = new WeakReference<>(context); handlerThread = new HandlerThread("clientTransportPlugin", Thread.MIN_PRIORITY); loadCdnFronts(context); + IPtProxy.setStateLocation(context.getApplicationContext().getCacheDir() + "/pt_state"); } @Override @@ -96,18 +97,39 @@ public class ClientTransportPlugin implements ClientTransportPluginInterface, Pr private void startConnectionAttempt(boolean useAmpCache, @NonNull String logfilePath) { //this is using the current, default Tor snowflake infrastructure String target = getCdnFront("snowflake-target"); - String front = getCdnFront("snowflake-front"); + String fronts = getCdnFront("snowflake-fronts"); String stunServer = getCdnFront("snowflake-stun"); String ampCache = null; if (useAmpCache) { target = "https://snowflake-broker.torproject.net/"; ampCache = "https://cdn.ampproject.org/"; - front = "www.google.com"; + fronts = "www.google.com"; } - snowflakePort = IPtProxy.startSnowflake(stunServer, target, front, ampCache, logfilePath, false, false, true, 5); + + snowflakePort = startSnowflake(stunServer, target, fronts, ampCache, null, null, logfilePath, false, false, false, 5); Log.d(TAG, "startSnowflake running on port: " + snowflakePort); } +/** + StartSnowflake - Start IPtProxy's Snowflake client. + @param ice Comma-separated list of ICE servers. + @param url URL of signaling broker. + @param fronts Comma-separated list of front domains. + @param ampCache OPTIONAL. URL of AMP cache to use as a proxy for signaling. + Only needed when you want to do the rendezvous over AMP instead of a domain fronted server. + @param sqsQueueURL OPTIONAL. URL of SQS Queue to use as a proxy for signaling. + @param sqsCredsStr OPTIONAL. Credentials to access SQS Queue + @param logFile Name of log file. OPTIONAL. Defaults to no log. + @param logToStateDir Resolve the log file relative to Tor's PT state dir. + @param keepLocalAddresses Keep local LAN address ICE candidates. + @param unsafeLogging Prevent logs from being scrubbed. + @param maxPeers Capacity for number of multiplexed WebRTC peers. DEFAULTs to 1 if less than that. + @return Port number where Snowflake will listen on, if no error happens during start up. + */ + private long startSnowflake(String ice, String url, String fronts, String ampCache, String sqsQueueURL, String sqsCredsStr, String logFile, boolean logToStateDir, boolean keepLocalAddresses, boolean unsafeLogging, long maxPeers) { + return IPtProxy.startSnowflake(ice, url, fronts, ampCache, sqsQueueURL, sqsCredsStr, logFile, logToStateDir, keepLocalAddresses, unsafeLogging, maxPeers); + } + private void retryConnectionAttempt(boolean useAmpCache) { Log.d(TAG, ">> retryConnectionAttempt - " + (useAmpCache ? "amp cache" : "http domain fronting")); stopConnectionAttempt(); |