diff options
Diffstat (limited to 'app/src/main/java/de/blinkt')
10 files changed, 438 insertions, 72 deletions
diff --git a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java index 7b9003aa..f139fdc9 100644 --- a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java +++ b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java @@ -53,7 +53,6 @@ import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; -import de.blinkt.openvpn.core.Connection; import de.blinkt.openvpn.core.ExtAuthHelper; import de.blinkt.openvpn.core.NativeUtils; import de.blinkt.openvpn.core.OpenVPNService; @@ -63,9 +62,13 @@ import de.blinkt.openvpn.core.Preferences; import de.blinkt.openvpn.core.VPNLaunchHelper; import de.blinkt.openvpn.core.VpnStatus; import de.blinkt.openvpn.core.X509Utils; +import de.blinkt.openvpn.core.connection.Connection; +import de.blinkt.openvpn.core.connection.Obfs4Connection; +import de.blinkt.openvpn.core.connection.OpenvpnConnection; import se.leap.bitmaskclient.BuildConfig; import se.leap.bitmaskclient.R; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; import static se.leap.bitmaskclient.Constants.PROVIDER_PROFILE; public class VpnProfile implements Serializable, Cloneable { @@ -116,7 +119,7 @@ public class VpnProfile implements Serializable, Cloneable { public String mTLSAuthFilename; public String mClientKeyFilename; public String mCaFilename; - public boolean mUseLzo = true; + public boolean mUseLzo = false; public String mPKCS12Filename; public String mPKCS12Password; public boolean mUseTLSAuth = false; @@ -171,6 +174,7 @@ public class VpnProfile implements Serializable, Cloneable { // timestamp when the profile was last used public long mLastUsed; public String importedProfileHash; + //TODO: cleanup here /* Options no longer used in new profiles */ public String mServerName = "openvpn.example.com"; public String mServerPort = "1194"; @@ -181,16 +185,17 @@ public class VpnProfile implements Serializable, Cloneable { // set members to default values private UUID mUuid; private int mProfileVersion; + public String mGatewayIp; + public boolean mUsePluggableTransports; - - public VpnProfile(String name) { + public VpnProfile(String name, Connection.TransportType transportType) { mUuid = UUID.randomUUID(); mName = name; mProfileVersion = CURRENT_PROFILE_VERSION; mConnections = new Connection[1]; - mConnections[0] = new Connection(); mLastUsed = System.currentTimeMillis(); + mUsePluggableTransports = transportType == OBFS4; } public static String openVpnEscape(String unescaped) { @@ -292,6 +297,7 @@ public class VpnProfile implements Serializable, Cloneable { return mName; } + @Deprecated public void upgradeProfile() { if (mProfileVersion < 2) { /* default to the behaviour the OS used */ @@ -314,22 +320,23 @@ public class VpnProfile implements Serializable, Cloneable { } if (mProfileVersion < 7) { for (Connection c : mConnections) - if (c.mProxyType == null) - c.mProxyType = Connection.ProxyType.NONE; + if (c.getProxyType() == null) + c.setProxyType(Connection.ProxyType.NONE); } mProfileVersion = CURRENT_PROFILE_VERSION; } + @Deprecated private void moveOptionsToConnection() { mConnections = new Connection[1]; - Connection conn = new Connection(); + Connection conn = mUsePluggableTransports ? new Obfs4Connection() : new OpenvpnConnection(); - conn.mServerName = mServerName; - conn.mServerPort = mServerPort; - conn.mUseUdp = mUseUdp; - conn.mCustomConfiguration = ""; + conn.setServerName(mServerName); + conn.setServerPort(mServerPort); + conn.setUseUdp(mUseUdp); + conn.setCustomConfiguration(""); mConnections[0] = conn; @@ -425,7 +432,7 @@ public class VpnProfile implements Serializable, Cloneable { if (canUsePlainRemotes) { for (Connection conn : mConnections) { - if (conn.mEnabled) { + if (conn.isEnabled()) { cfg.append(conn.getConnectionBlock(configForOvpn3)); } } @@ -494,7 +501,8 @@ public class VpnProfile implements Serializable, Cloneable { if (!TextUtils.isEmpty(mCrlFilename)) cfg.append(insertFileData("crl-verify", mCrlFilename)); - if (mUseLzo) { + // compression does not work in conjunction with shapeshifter-dispatcher so far + if (mUseLzo && !mUsePluggableTransports) { cfg.append("comp-lzo\n"); } @@ -586,7 +594,7 @@ public class VpnProfile implements Serializable, Cloneable { if (mAuthenticationType != TYPE_STATICKEYS) { if (mCheckRemoteCN) { if (mRemoteCN == null || mRemoteCN.equals("")) - cfg.append("verify-x509-name ").append(openVpnEscape(mConnections[0].mServerName)).append(" name\n"); + cfg.append("verify-x509-name ").append(openVpnEscape(mConnections[0].getServerName())).append(" name\n"); else switch (mX509AuthType) { @@ -660,7 +668,7 @@ public class VpnProfile implements Serializable, Cloneable { if (!canUsePlainRemotes) { cfg.append("# Connection Options are at the end to allow global options (and global custom options) to influence connection blocks\n"); for (Connection conn : mConnections) { - if (conn.mEnabled) { + if (conn.isEnabled()) { cfg.append("<connection>\n"); cfg.append(conn.getConnectionBlock(configForOvpn3)); cfg.append("</connection>\n"); @@ -985,7 +993,7 @@ public class VpnProfile implements Serializable, Cloneable { boolean noRemoteEnabled = true; for (Connection c : mConnections) { - if (c.mEnabled) + if (c.isEnabled()) noRemoteEnabled = false; } @@ -1000,12 +1008,12 @@ public class VpnProfile implements Serializable, Cloneable { return R.string.openvpn3_pkcs12; } for (Connection conn : mConnections) { - if (conn.mProxyType == Connection.ProxyType.ORBOT || conn.mProxyType == Connection.ProxyType.SOCKS5) + if (conn.getProxyType() == Connection.ProxyType.ORBOT || conn.getProxyType() == Connection.ProxyType.SOCKS5) return R.string.openvpn3_socksproxy; } } for (Connection c : mConnections) { - if (c.mProxyType == Connection.ProxyType.ORBOT) { + if (c.getProxyType() == Connection.ProxyType.ORBOT) { if (usesExtraProxyOptions()) return R.string.error_orbot_and_proxy_options; if (!OrbotHelper.checkTorReceier(context)) 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 0148bfb7..5ccd83dd 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java +++ b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java @@ -13,9 +13,21 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.io.StringReader; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +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.OpenvpnConnection; +import se.leap.bitmaskclient.pluggableTransports.Obfs4Options; + +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; //! Openvpn Config FIle Parser, probably not 100% accurate but close enough @@ -128,6 +140,7 @@ public class ConfigParser { private HashMap<String, Vector<Vector<String>>> options = new HashMap<>(); private HashMap<String, Vector<String>> meta = new HashMap<String, Vector<String>>(); private String auth_user_pass_file; + private Obfs4Options obfs4Options; static public void useEmbbedUserAuth(VpnProfile np, String inlinedata) { String data = VpnProfile.getEmbeddedContent(inlinedata); @@ -142,9 +155,9 @@ public class ConfigParser { String data = VpnProfile.getEmbeddedContent(inlinedata); String[] parts = data.split("\n"); if (parts.length >= 2) { - c.mProxyAuthUser = parts[0]; - c.mProxyAuthPassword = parts[1]; - c.mUseProxyAuth = true; + c.setProxyAuthUser(parts[0]); + c.setProxyAuthPassword(parts[1]); + c.setUseProxyAuth(true); } } @@ -338,9 +351,9 @@ public class ConfigParser { // This method is far too long @SuppressWarnings("ConstantConditions") - public VpnProfile convertProfile() throws ConfigParseError, IOException { + public VpnProfile convertProfile(Connection.TransportType transportType) throws ConfigParseError, IOException { boolean noauthtypeset = true; - VpnProfile np = new VpnProfile(CONVERTED_PROFILE); + VpnProfile np = new VpnProfile(CONVERTED_PROFILE, transportType); // Pull, client, tls-client np.clearDefaults(); @@ -443,6 +456,7 @@ public class ConfigParser { if (redirectPrivate != null) { checkRedirectParameters(np, redirectPrivate, false); } + Vector<String> dev = getOption("dev", 1, 1); Vector<String> devtype = getOption("dev-type", 1, 1); @@ -468,7 +482,6 @@ public class ConfigParser { } } - Vector<String> tunmtu = getOption("tun-mtu", 1, 1); if (tunmtu != null) { @@ -479,14 +492,12 @@ public class ConfigParser { } } - Vector<String> mode = getOption("mode", 1, 1); if (mode != null) { if (!mode.get(1).equals("p2p")) throw new ConfigParseError("Invalid mode for --mode specified, need p2p"); } - Vector<Vector<String>> dhcpoptions = getAllOption("dhcp-option", 2, 2); if (dhcpoptions != null) { for (Vector<String> dhcpoption : dhcpoptions) { @@ -521,8 +532,10 @@ public class ConfigParser { if (getOption("float", 0, 0) != null) np.mUseFloat = true; - if (getOption("comp-lzo", 0, 1) != null) - np.mUseLzo = true; + Vector<String> useLzo = getOption("comp-lzo", 0, 1); + if (useLzo != null) { + np.mUseLzo = Boolean.valueOf(useLzo.get(1)); + } Vector<String> cipher = getOption("cipher", 1, 1); if (cipher != null) @@ -532,7 +545,6 @@ public class ConfigParser { if (auth != null) np.mAuth = auth.get(1); - Vector<String> ca = getOption("ca", 1, 1); if (ca != null) { np.mCaFilename = ca.get(1); @@ -544,6 +556,7 @@ public class ConfigParser { np.mAuthenticationType = VpnProfile.TYPE_CERTIFICATES; noauthtypeset = false; } + Vector<String> key = getOption("key", 1, 1); if (key != null) np.mClientKeyFilename = key.get(1); @@ -604,8 +617,7 @@ public class ConfigParser { np.mVerb = verb.get(1); } - - if (getOption("nobind", 0, 0) != null) + if (getOption("nobind", 0, 1) != null) np.mNobind = true; if (getOption("persist-tun", 0, 0) != null) @@ -674,8 +686,7 @@ public class ConfigParser { } - - Pair<Connection, Connection[]> conns = parseConnectionOptions(null); + Pair<Connection, Connection[]> conns = parseConnectionOptions(null, transportType); np.mConnections = conns.second; Vector<Vector<String>> connectionBlocks = getAllOption("connection", 1, 1); @@ -698,6 +709,7 @@ public class ConfigParser { connIndex++; } } + if (getOption("remote-random", 0, 0) != null) np.mRemoteRandom = true; @@ -713,8 +725,8 @@ public class ConfigParser { throw new ConfigParseError(String.format("Unknown protocol %s in proto-force", protoToDisable)); for (Connection conn : np.mConnections) - if (conn.mUseUdp == disableUDP) - conn.mEnabled = false; + if (conn.isUseUdp() == disableUDP) + conn.setEnabled(false); } // Parse OpenVPN Access Server extra @@ -740,20 +752,21 @@ public class ConfigParser { return TextUtils.join(s, str); } + public void setObfs4Options(Obfs4Options obfs4Options) { + this.obfs4Options = obfs4Options; + } + private Pair<Connection, Connection[]> parseConnection(String connection, Connection defaultValues) throws IOException, ConfigParseError { // Parse a connection Block as a new configuration file - ConfigParser connectionParser = new ConfigParser(); StringReader reader = new StringReader(connection.substring(VpnProfile.INLINE_TAG.length())); connectionParser.parseConfig(reader); - Pair<Connection, Connection[]> conn = connectionParser.parseConnectionOptions(defaultValues); - - return conn; + return connectionParser.parseConnectionOptions(defaultValues, defaultValues.getTransportType()); } - private Pair<Connection, Connection[]> parseConnectionOptions(Connection connDefault) throws ConfigParseError { + private Pair<Connection, Connection[]> parseConnectionOptions(Connection connDefault, Connection.TransportType transportType) throws ConfigParseError { Connection conn; if (connDefault != null) try { @@ -763,27 +776,27 @@ public class ConfigParser { return null; } else - conn = new Connection(); + conn = transportType == OBFS4 ? new Obfs4Connection(obfs4Options) : new OpenvpnConnection(); Vector<String> port = getOption("port", 1, 1); if (port != null) { - conn.mServerPort = port.get(1); + conn.setServerPort(port.get(1)); } Vector<String> rport = getOption("rport", 1, 1); if (rport != null) { - conn.mServerPort = rport.get(1); + conn.setServerPort(rport.get(1)); } Vector<String> proto = getOption("proto", 1, 1); if (proto != null) { - conn.mUseUdp = isUdpProto(proto.get(1)); + conn.setUseUdp(isUdpProto(proto.get(1))); } Vector<String> connectTimeout = getOption("connect-timeout", 1, 1); if (connectTimeout != null) { try { - conn.mConnectTimeout = Integer.parseInt(connectTimeout.get(1)); + conn.setConnectTimeout(Integer.parseInt(connectTimeout.get(1))); } catch (NumberFormatException nfe) { throw new ConfigParseError(String.format("Argument to connect-timeout (%s) must to be an integer: %s", connectTimeout.get(1), nfe.getLocalizedMessage())); @@ -797,16 +810,16 @@ public class ConfigParser { if (proxy != null) { if (proxy.get(0).equals("socks-proxy")) { - conn.mProxyType = Connection.ProxyType.SOCKS5; + conn.setProxyType(Connection.ProxyType.SOCKS5); // socks defaults to 1080, http always sets port - conn.mProxyPort = "1080"; + conn.setProxyPort("1080"); } else { - conn.mProxyType = Connection.ProxyType.HTTP; + conn.setProxyType(Connection.ProxyType.HTTP); } - conn.mProxyName = proxy.get(1); + conn.setProxyName(proxy.get(1)); if (proxy.size() >= 3) - conn.mProxyPort = proxy.get(2); + conn.setProxyPort(proxy.get(2)); } Vector<String> httpproxyauthhttp = getOption("http-proxy-user-pass", 1, 1); @@ -817,21 +830,19 @@ public class ConfigParser { // Parse remote config Vector<Vector<String>> remotes = getAllOption("remote", 1, 3); - - Vector <String> optionsToRemove = new Vector<>(); // Assume that we need custom options if connectionDefault are set or in the connection specific set for (Map.Entry<String, Vector<Vector<String>>> option : options.entrySet()) { if (connDefault != null || connectionOptionsSet.contains(option.getKey())) { - conn.mCustomConfiguration += getOptionStrings(option.getValue()); + conn.setCustomConfiguration(conn.getCustomConfiguration() + getOptionStrings(option.getValue())); optionsToRemove.add(option.getKey()); } } for (String o: optionsToRemove) options.remove(o); - if (!(conn.mCustomConfiguration == null || "".equals(conn.mCustomConfiguration.trim()))) - conn.mUseCustomConfig = true; + if (!(conn.getCustomConfiguration() == null || "".equals(conn.getCustomConfiguration().trim()))) + conn.setUseCustomConfig(true); // Make remotes empty to simplify code if (remotes == null) @@ -849,11 +860,11 @@ public class ConfigParser { } switch (remote.size()) { case 4: - connections[i].mUseUdp = isUdpProto(remote.get(3)); + connections[i].setUseUdp(isUdpProto(remote.get(3))); case 3: - connections[i].mServerPort = remote.get(2); + connections[i].setServerPort(remote.get(2)); case 2: - connections[i].mServerName = remote.get(1); + connections[i].setServerName(remote.get(1)); } i++; } diff --git a/app/src/main/java/de/blinkt/openvpn/core/ConnectionInterface.java b/app/src/main/java/de/blinkt/openvpn/core/ConnectionInterface.java new file mode 100644 index 00000000..70b4b4ec --- /dev/null +++ b/app/src/main/java/de/blinkt/openvpn/core/ConnectionInterface.java @@ -0,0 +1,15 @@ +package de.blinkt.openvpn.core; + +import java.io.Serializable; + +/** + * Created by cyberta on 11.03.19. + */ + +public interface ConnectionInterface { + + String getConnectionBlock(boolean isOpenVPN3); + boolean usesExtraProxyOptions(); + boolean isOnlyRemote(); + int getTimeout(); +} diff --git a/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java b/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java index 6b633c34..a66b7b51 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java +++ b/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java @@ -20,6 +20,8 @@ public class NativeUtils { { if (isRoboUnitTest()) return "ROBO"; + else if (isUnitTest()) + return "JUNIT"; else return getJNIAPI(); } @@ -34,7 +36,7 @@ public class NativeUtils { public static native double[] getOpenSSLSpeed(String algorithm, int testnum); static { - if (!isRoboUnitTest()) { + if (!isRoboUnitTest() && !isUnitTest()) { System.loadLibrary("opvpnutil"); if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) System.loadLibrary("jbcrypto"); @@ -44,4 +46,13 @@ public class NativeUtils { public static boolean isRoboUnitTest() { return "robolectric".equals(Build.FINGERPRINT); } + + public static boolean isUnitTest() { + try { + Class.forName("se.leap.bitmaskclient.testutils.MockHelper"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } } 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 82c4e1df..e446021f 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -42,8 +42,11 @@ import java.util.Vector; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.VpnStatus.ByteCountListener; import de.blinkt.openvpn.core.VpnStatus.StateListener; +import de.blinkt.openvpn.core.connection.Connection; +import de.blinkt.openvpn.core.connection.Obfs4Connection; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.VpnNotificationManager; +import se.leap.bitmaskclient.pluggableTransports.Shapeshifter; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTED; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT; @@ -52,6 +55,7 @@ import static se.leap.bitmaskclient.Constants.PROVIDER_PROFILE; public class OpenVPNService extends VpnService implements StateListener, Callback, ByteCountListener, IOpenVPNServiceInternal, VpnNotificationManager.VpnServiceCallback { + public static final String TAG = OpenVPNService.class.getSimpleName(); public static final String START_SERVICE = "de.blinkt.openvpn.START_SERVICE"; public static final String START_SERVICE_STICKY = "de.blinkt.openvpn.START_SERVICE_STICKY"; public static final String ALWAYS_SHOW_NOTIFICATION = "de.blinkt.openvpn.NOTIFICATION_ALWAYS_VISIBLE"; @@ -62,7 +66,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac public final static String ORBOT_PACKAGE_NAME = "org.torproject.android"; private static final String PAUSE_VPN = "de.blinkt.openvpn.PAUSE_VPN"; private static final String RESUME_VPN = "se.leap.bitmaskclient.RESUME_VPN"; - private static final String TAG = OpenVPNService.class.getSimpleName(); private static boolean mNotificationAlwaysVisible = false; private final Vector<String> mDnslist = new Vector<>(); private final NetworkSpace mRoutes = new NetworkSpace(); @@ -85,6 +88,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac private Toast mlastToast; private Runnable mOpenVPNThread; private VpnNotificationManager notificationManager; + private Shapeshifter shapeshifter; private static final int PRIORITY_MIN = -2; private static final int PRIORITY_DEFAULT = 0; @@ -242,6 +246,9 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac if(isVpnRunning()) { if (getManagement() != null && getManagement().stopVPN(replaceConnection)) { if (!replaceConnection) { + if (shapeshifter != null) { + shapeshifter.stop(); + } VpnStatus.updateStateString("NOPROCESS", "VPN STOPPED", R.string.state_noprocess, ConnectionStatus.LEVEL_NOTCONNECTED); } return true; @@ -249,6 +256,9 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac return false; } else { if (!replaceConnection) { + if (shapeshifter != null) { + shapeshifter.stop(); + } VpnStatus.updateStateString("NOPROCESS", "VPN STOPPED", R.string.state_noprocess, ConnectionStatus.LEVEL_NOTCONNECTED); return true; } @@ -307,6 +317,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac VpnStatus.updateStateString("VPN_GENERATE_CONFIG", "", R.string.building_configration, ConnectionStatus.LEVEL_START); notificationManager.buildOpenVpnNotification( mProfile != null ? mProfile.mName : "", + mProfile != null && mProfile.mUsePluggableTransports, VpnStatus.getLastCleanLogMessage(this), VpnStatus.getLastCleanLogMessage(this), ConnectionStatus.LEVEL_START, @@ -366,10 +377,26 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac /** * see change above (l. 292 ff) */ + //TODO: investigate how connections[n] with n>0 get called during vpn setup (on connection refused?) + // Do we need to check if there's any obfs4 connection in mProfile.mConnections and start + // the dispatcher here? Can we start the dispatcher at a later point of execution, e.g. when + // connections[n], n>0 gets choosen? + + Connection connection = mProfile.mConnections[0]; + + if (mProfile.mUsePluggableTransports) { + Obfs4Connection obfs4Connection = (Obfs4Connection) connection; + shapeshifter = new Shapeshifter(obfs4Connection.getDispatcherOptions()); + if (!shapeshifter.start()) { + //TODO: implement useful error handling + Log.e(TAG, "Cannot initialize shapeshifter dispatcher for obfs4 connection. Shutting down."); + VpnStatus.logError("Cannot initialize shapeshifter dispatcher for obfs4 connection. Shutting down."); + } + } + VpnStatus.logInfo(R.string.building_configration); VpnStatus.updateStateString("VPN_GENERATE_CONFIG", "", R.string.building_configration, ConnectionStatus.LEVEL_START); - try { mProfile.writeConfigFile(this); } catch (IOException e) { @@ -743,7 +770,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac boolean profileUsesOrBot = false; for (Connection c : mProfile.mConnections) { - if (c.mProxyType == Connection.ProxyType.ORBOT) + if (c.getProxyType() == Connection.ProxyType.ORBOT) profileUsesOrBot = true; } @@ -951,6 +978,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac // Does not work :( notificationManager.buildOpenVpnNotification( mProfile != null ? mProfile.mName : "", + mProfile != null && mProfile.mUsePluggableTransports, VpnStatus.getLastCleanLogMessage(this), VpnStatus.getLastCleanLogMessage(this), level, @@ -982,6 +1010,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac humanReadableByteCount(diffOut / OpenVPNManagement.mBytecountInterval, true, getResources())); notificationManager.buildOpenVpnNotification( mProfile != null ? mProfile.mName : "", + mProfile != null && mProfile.mUsePluggableTransports, netstat, null, LEVEL_CONNECTED, @@ -1025,6 +1054,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac VpnStatus.updateStateString("NEED", "need " + needed, resid, LEVEL_WAITING_FOR_USER_INPUT); notificationManager.buildOpenVpnNotification( mProfile != null ? mProfile.mName : "", + mProfile != null && mProfile.mUsePluggableTransports, getString(resid), getString(resid), LEVEL_WAITING_FOR_USER_INPUT, diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java index 4f7a5bda..91cc66bc 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java @@ -15,9 +15,10 @@ import android.os.Handler; import android.os.ParcelFileDescriptor; import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; -import android.system.ErrnoException; import android.system.Os; import android.util.Log; + +import de.blinkt.openvpn.core.connection.Connection; import se.leap.bitmaskclient.R; import de.blinkt.openvpn.VpnProfile; @@ -452,10 +453,10 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { if (mProfile.mConnections.length > connectionEntryNumber) { Connection connection = mProfile.mConnections[connectionEntryNumber]; - proxyType = connection.mProxyType; - proxyname = connection.mProxyName; - proxyport = connection.mProxyPort; - proxyUseAuth = connection.mUseProxyAuth; + proxyType = connection.getProxyType(); + proxyname = connection.getProxyName(); + proxyport = connection.getProxyPort(); + proxyUseAuth = connection.isUseProxyAuth(); // Use transient variable to remember http user/password mCurrentProxyConnection = connection; @@ -696,8 +697,8 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { } else if (needed.equals("HTTP Proxy")) { if( mCurrentProxyConnection != null) { - pw = mCurrentProxyConnection.mProxyAuthPassword; - username = mCurrentProxyConnection.mProxyAuthUser; + pw = mCurrentProxyConnection.getProxyAuthPassword(); + username = mCurrentProxyConnection.getProxyAuthUser(); } } if (pw != null) { @@ -782,7 +783,6 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { boolean stopSucceed = stopOpenVPN(); if (stopSucceed) { mShuttingDown = true; - } return stopSucceed; } diff --git a/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java b/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java diff --git a/app/src/main/java/de/blinkt/openvpn/core/connection/Connection.java b/app/src/main/java/de/blinkt/openvpn/core/connection/Connection.java new file mode 100644 index 00000000..a318e55d --- /dev/null +++ b/app/src/main/java/de/blinkt/openvpn/core/connection/Connection.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2012-2016 Arne Schwabe + * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt + */ + +package de.blinkt.openvpn.core.connection; + +import android.text.TextUtils; + +import java.io.Serializable; +import java.util.Locale; + +public abstract class Connection implements Serializable, Cloneable { + private String mServerName = "openvpn.example.com"; + private String mServerPort = "1194"; + private boolean mUseUdp = true; + private String mCustomConfiguration = ""; + private boolean mUseCustomConfig = false; + private boolean mEnabled = true; + private int mConnectTimeout = 0; + private static final int CONNECTION_DEFAULT_TIMEOUT = 120; + private ProxyType mProxyType = ProxyType.NONE; + private String mProxyName = "proxy.example.com"; + private String mProxyPort = "8080"; + + private boolean mUseProxyAuth; + private String mProxyAuthUser = null; + private String mProxyAuthPassword = null; + + public enum ProxyType { + NONE, + HTTP, + SOCKS5, + ORBOT + } + + public enum TransportType { + OBFS4("obfs4"), + OPENVPN("openvpn"); + + String transport; + + TransportType(String transportType) { + this.transport = transportType; + } + + @Override + public String toString() { + return transport; + } + } + + + private static final long serialVersionUID = 92031902903829089L; + + + public String getConnectionBlock(boolean isOpenVPN3) { + String cfg = ""; + + // Server Address + cfg += "remote "; + cfg += mServerName; + cfg += " "; + cfg += mServerPort; + if (mUseUdp) + cfg += " udp\n"; + else + cfg += " tcp-client\n"; + + if (mConnectTimeout != 0) + cfg += String.format(Locale.US, " connect-timeout %d\n", mConnectTimeout); + + // OpenVPN 2.x manages proxy connection via management interface + if ((isOpenVPN3 || usesExtraProxyOptions()) && mProxyType == ProxyType.HTTP) + { + cfg+=String.format(Locale.US,"http-proxy %s %s\n", mProxyName, mProxyPort); + if (mUseProxyAuth) + cfg+=String.format(Locale.US, "<http-proxy-user-pass>\n%s\n%s\n</http-proxy-user-pass>\n", mProxyAuthUser, mProxyAuthPassword); + } + if (usesExtraProxyOptions() && mProxyType == ProxyType.SOCKS5) { + cfg+=String.format(Locale.US,"socks-proxy %s %s\n", mProxyName, mProxyPort); + } + + if (!TextUtils.isEmpty(mCustomConfiguration) && mUseCustomConfig) { + cfg += mCustomConfiguration; + cfg += "\n"; + } + + + return cfg; + } + + public boolean usesExtraProxyOptions() { + return (mUseCustomConfig && mCustomConfiguration.contains("http-proxy-option ")); + } + + + @Override + public Connection clone() throws CloneNotSupportedException { + return (Connection) super.clone(); + } + + public boolean isOnlyRemote() { + return TextUtils.isEmpty(mCustomConfiguration) || !mUseCustomConfig; + } + + public int getTimeout() { + if (mConnectTimeout <= 0) + return CONNECTION_DEFAULT_TIMEOUT; + else + return mConnectTimeout; + } + + public String getServerName() { + return mServerName; + } + + public void setServerName(String mServerName) { + this.mServerName = mServerName; + } + + public String getServerPort() { + return mServerPort; + } + + public void setServerPort(String serverPort) { + this.mServerPort = serverPort; + } + + public boolean isUseUdp() { + return mUseUdp; + } + + public void setUseUdp(boolean useUdp) { + this.mUseUdp = useUdp; + } + + public String getCustomConfiguration() { + return mCustomConfiguration; + } + + public void setCustomConfiguration(String customConfiguration) { + this.mCustomConfiguration = customConfiguration; + } + + public boolean isUseCustomConfig() { + return mUseCustomConfig; + } + + public void setUseCustomConfig(boolean useCustomConfig) { + this.mUseCustomConfig = useCustomConfig; + } + + public boolean isEnabled() { + return mEnabled; + } + + public void setEnabled(boolean enabled) { + this.mEnabled = enabled; + } + + public int getConnectTimeout() { + return mConnectTimeout; + } + + public void setConnectTimeout(int connectTimeout) { + this.mConnectTimeout = connectTimeout; + } + + public ProxyType getProxyType() { + return mProxyType; + } + + public void setProxyType(ProxyType proxyType) { + this.mProxyType = proxyType; + } + + public String getProxyName() { + return mProxyName; + } + + public void setProxyName(String proxyName) { + this.mProxyName = proxyName; + } + + public String getProxyPort() { + return mProxyPort; + } + + public void setProxyPort(String proxyPort) { + this.mProxyPort = proxyPort; + } + + public boolean isUseProxyAuth() { + return mUseProxyAuth; + } + + public void setUseProxyAuth(boolean useProxyAuth) { + this.mUseProxyAuth = useProxyAuth; + } + + public String getProxyAuthUser() { + return mProxyAuthUser; + } + + public void setProxyAuthUser(String proxyAuthUser) { + this.mProxyAuthUser = proxyAuthUser; + } + + public String getProxyAuthPassword() { + return mProxyAuthPassword; + } + + public void setProxyAuthPassword(String proxyAuthPassword) { + this.mProxyAuthPassword = proxyAuthPassword; + } + + public abstract TransportType getTransportType(); +} 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 new file mode 100644 index 00000000..a2f86e05 --- /dev/null +++ b/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4Connection.java @@ -0,0 +1,59 @@ +package de.blinkt.openvpn.core.connection; + +import se.leap.bitmaskclient.pluggableTransports.Obfs4Options; + +import static se.leap.bitmaskclient.pluggableTransports.Dispatcher.DISPATCHER_IP; +import static se.leap.bitmaskclient.pluggableTransports.Dispatcher.DISPATCHER_PORT; + +/** + * Created by cyberta on 08.03.19. + */ + +public class Obfs4Connection extends Connection { + + private static final String TAG = Obfs4Connection.class.getName(); + private Obfs4Options options; + + public Obfs4Connection(Obfs4Options options) { + setUseUdp(false); + setServerName(DISPATCHER_IP); + setServerPort(DISPATCHER_PORT); + setProxyName(""); + setProxyPort(""); + setProxyAuthUser(null); + setProxyAuthPassword(null); + setProxyType(ProxyType.NONE); + setUseProxyAuth(false); + this.options = options; + } + + @Deprecated + public Obfs4Connection() { + setUseUdp(false); + setServerName(DISPATCHER_IP); + setServerPort(DISPATCHER_PORT); + setProxyName(""); + setProxyPort(""); + setProxyAuthUser(null); + setProxyAuthPassword(null); + setProxyType(ProxyType.NONE); + setUseProxyAuth(false); } + + @Override + public Connection clone() throws CloneNotSupportedException { + Obfs4Connection connection = (Obfs4Connection) super.clone(); + connection.options = this.options; + return connection; + } + + @Override + public TransportType getTransportType() { + return TransportType.OBFS4; + } + + + public Obfs4Options getDispatcherOptions() { + return options; + } + +} diff --git a/app/src/main/java/de/blinkt/openvpn/core/connection/OpenvpnConnection.java b/app/src/main/java/de/blinkt/openvpn/core/connection/OpenvpnConnection.java new file mode 100644 index 00000000..3a3fd0c3 --- /dev/null +++ b/app/src/main/java/de/blinkt/openvpn/core/connection/OpenvpnConnection.java @@ -0,0 +1,13 @@ +package de.blinkt.openvpn.core.connection; + +/** + * Created by cyberta on 11.03.19. + */ + +public class OpenvpnConnection extends Connection { + + @Override + public TransportType getTransportType() { + return TransportType.OPENVPN; + } +} |