summaryrefslogtreecommitdiff
path: root/app/src/main/java/de/blinkt
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/de/blinkt')
-rw-r--r--app/src/main/java/de/blinkt/openvpn/VpnProfile.java46
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java93
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ConnectionInterface.java15
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java13
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java36
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java16
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java0
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/connection/Connection.java219
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4Connection.java59
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/connection/OpenvpnConnection.java13
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;
+ }
+}