summaryrefslogtreecommitdiff
path: root/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java')
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java379
1 files changed, 213 insertions, 166 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 9889754d..0148bfb7 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
@@ -5,17 +5,15 @@
package de.blinkt.openvpn.core;
-import android.text.TextUtils;
+import android.os.Build;
import android.support.v4.util.Pair;
+import android.text.TextUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Vector;
+import java.util.*;
import de.blinkt.openvpn.VpnProfile;
@@ -29,10 +27,127 @@ public class ConfigParser {
public static final String CONVERTED_PROFILE = "converted Profile";
- private HashMap<String, Vector<Vector<String>>> options = new HashMap<String, Vector<Vector<String>>>();
+ final String[] unsupportedOptions = {"config",
+ "tls-server"
+
+ };
+ // Ignore all scripts
+ // in most cases these won't work and user who wish to execute scripts will
+ // figure out themselves
+ private final String[] ignoreOptions = {"tls-client",
+ "allow-recursive-routing",
+ "askpass",
+ "auth-nocache",
+ "up",
+ "down",
+ "route-up",
+ "ipchange",
+ "route-pre-down",
+ "auth-user-pass-verify",
+ "block-outside-dns",
+ "client-cert-not-required",
+ "dhcp-release",
+ "dhcp-renew",
+ "dh",
+ "group",
+ "ip-win32",
+ "ifconfig-nowarn",
+ "management-hold",
+ "management",
+ "management-client",
+ "management-query-remote",
+ "management-query-passwords",
+ "management-query-proxy",
+ "management-external-key",
+ "management-forget-disconnect",
+ "management-signal",
+ "management-log-cache",
+ "management-up-down",
+ "management-client-user",
+ "management-client-group",
+ "pause-exit",
+ "preresolve",
+ "plugin",
+ "machine-readable-output",
+ "persist-key",
+ "push",
+ "register-dns",
+ "route-delay",
+ "route-gateway",
+ "route-metric",
+ "route-method",
+ "status",
+ "script-security",
+ "show-net-up",
+ "suppress-timestamps",
+ "tap-sleep",
+ "tmp-dir",
+ "tun-ipv6",
+ "topology",
+ "user",
+ "win-sys",
+ };
+ private final String[][] ignoreOptionsWithArg =
+ {
+ {"setenv", "IV_GUI_VER"},
+ {"setenv", "IV_PLAT_VER"},
+ {"setenv", "IV_OPENVPN_GUI_VERSION"},
+ {"engine", "dynamic"},
+ {"setenv", "CLIENT_CERT"},
+ {"resolv-retry", "60"}
+ };
+ private final String[] connectionOptions = {
+ "local",
+ "remote",
+ "float",
+ "port",
+ "connect-retry",
+ "connect-timeout",
+ "connect-retry-max",
+ "link-mtu",
+ "tun-mtu",
+ "tun-mtu-extra",
+ "fragment",
+ "mtu-disc",
+ "local-port",
+ "remote-port",
+ "bind",
+ "nobind",
+ "proto",
+ "http-proxy",
+ "http-proxy-retry",
+ "http-proxy-timeout",
+ "http-proxy-option",
+ "socks-proxy",
+ "socks-proxy-retry",
+ "http-proxy-user-pass",
+ "explicit-exit-notify",
+ };
+ private HashSet<String> connectionOptionsSet = new HashSet<>(Arrays.asList(connectionOptions));
+
+ 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;
+ static public void useEmbbedUserAuth(VpnProfile np, String inlinedata) {
+ String data = VpnProfile.getEmbeddedContent(inlinedata);
+ String[] parts = data.split("\n");
+ if (parts.length >= 2) {
+ np.mUsername = parts[0];
+ np.mPassword = parts[1];
+ }
+ }
+
+ static public void useEmbbedHttpAuth(Connection c, String inlinedata) {
+ 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;
+ }
+ }
+
public void parseConfig(Reader reader) throws IOException, ConfigParseError {
HashMap<String, String> optionAliases = new HashMap<>();
@@ -76,7 +191,7 @@ public class ConfigParser {
checkinlinefile(args, br);
String optionname = args.get(0);
- if (optionAliases.get(optionname)!=null)
+ if (optionAliases.get(optionname) != null)
optionname = optionAliases.get(optionname);
if (!options.containsKey(optionname)) {
@@ -119,8 +234,8 @@ public class ConfigParser {
}
} while (true);
- if(inlinefile.endsWith("\n"))
- inlinefile = inlinefile.substring(0, inlinefile.length()-1);
+ if (inlinefile.endsWith("\n"))
+ inlinefile = inlinefile.substring(0, inlinefile.length() - 1);
args.clear();
args.add(argname);
@@ -133,11 +248,6 @@ public class ConfigParser {
return auth_user_pass_file;
}
- enum linestate {
- initial,
- readin_single_quote, reading_quoted, reading_unquoted, done
- }
-
private boolean space(char c) {
// I really hope nobody is using zero bytes inside his/her config file
// to sperate parameter but here we go:
@@ -145,15 +255,6 @@ public class ConfigParser {
}
- public static class ConfigParseError extends Exception {
- private static final long serialVersionUID = -60L;
-
- public ConfigParseError(String msg) {
- super(msg);
- }
- }
-
-
// adapted openvpn's parse function to java
private Vector<String> parseline(String line) throws ConfigParseError {
Vector<String> parameters = new Vector<String>();
@@ -226,7 +327,7 @@ public class ConfigParser {
backslash = false;
}
- /* store parameter character */
+ /* store parameter character */
if (out != 0) {
currentarg += out;
}
@@ -235,105 +336,6 @@ public class ConfigParser {
return parameters;
}
-
- final String[] unsupportedOptions = {"config",
- "tls-server"
-
- };
-
- // Ignore all scripts
- // in most cases these won't work and user who wish to execute scripts will
- // figure out themselves
- final String[] ignoreOptions = {"tls-client",
- "askpass",
- "auth-nocache",
- "up",
- "down",
- "route-up",
- "ipchange",
- "route-up",
- "route-pre-down",
- "auth-user-pass-verify",
- "block-outside-dns",
- "dhcp-release",
- "dhcp-renew",
- "dh",
- "group",
- "allow-recursive-routing",
- "ip-win32",
- "ifconfig-nowarn",
- "management-hold",
- "management",
- "management-client",
- "management-query-remote",
- "management-query-passwords",
- "management-query-proxy",
- "management-external-key",
- "management-forget-disconnect",
- "management-signal",
- "management-log-cache",
- "management-up-down",
- "management-client-user",
- "management-client-group",
- "pause-exit",
- "preresolve",
- "plugin",
- "machine-readable-output",
- "persist-key",
- "push",
- "register-dns",
- "route-delay",
- "route-gateway",
- "route-metric",
- "route-method",
- "status",
- "script-security",
- "show-net-up",
- "suppress-timestamps",
- "tmp-dir",
- "tun-ipv6",
- "topology",
- "user",
- "win-sys",
- };
-
- final String[][] ignoreOptionsWithArg =
- {
- {"setenv", "IV_GUI_VER"},
- {"setenv", "IV_OPENVPN_GUI_VERSION"},
- {"engine", "dynamic"},
- {"setenv", "CLIENT_CERT"},
- {"resolve-retry","60"}
- };
-
- final String[] connectionOptions = {
- "local",
- "remote",
- "float",
- "port",
- "connect-retry",
- "connect-timeout",
- "connect-retry-max",
- "link-mtu",
- "tun-mtu",
- "tun-mtu-extra",
- "fragment",
- "mtu-disc",
- "local-port",
- "remote-port",
- "bind",
- "nobind",
- "proto",
- "http-proxy",
- "http-proxy-retry",
- "http-proxy-timeout",
- "http-proxy-option",
- "socks-proxy",
- "socks-proxy-retry",
- "explicit-exit-notify",
- };
-
-
// This method is far too long
@SuppressWarnings("ConstantConditions")
public VpnProfile convertProfile() throws ConfigParseError, IOException {
@@ -403,8 +405,8 @@ public class ConfigParser {
}
Vector<String> routeNoPull = getOption("route-nopull", 0, 0);
- if (routeNoPull!=null)
- np.mRoutenopull=true;
+ if (routeNoPull != null)
+ np.mRoutenopull = true;
// Also recognize tls-auth [inline] direction ...
Vector<Vector<String>> tlsauthoptions = getAllOption("tls-auth", 1, 2);
@@ -426,7 +428,7 @@ public class ConfigParser {
np.mTLSAuthDirection = direction.get(1);
Vector<String> tlscrypt = getOption("tls-crypt", 1, 1);
- if (tlscrypt!=null) {
+ if (tlscrypt != null) {
np.mUseTLSAuth = true;
np.mTLSAuthFilename = tlscrypt.get(1);
np.mTLSAuthDirection = "tls-crypt";
@@ -467,7 +469,7 @@ public class ConfigParser {
}
- Vector<String> tunmtu = getOption("mtu", 1, 1);
+ Vector<String> tunmtu = getOption("tun-mtu", 1, 1);
if (tunmtu != null) {
try {
@@ -478,7 +480,6 @@ public class ConfigParser {
}
-
Vector<String> mode = getOption("mode", 1, 1);
if (mode != null) {
if (!mode.get(1).equals("p2p"))
@@ -592,9 +593,9 @@ public class ConfigParser {
}
- Vector<String> x509usernamefield = getOption("x509-username-field", 1,1);
- if (x509usernamefield!=null) {
- np.mx509UsernameField = x509usernamefield.get(1);
+ Vector<String> x509usernamefield = getOption("x509-username-field", 1, 1);
+ if (x509usernamefield != null) {
+ np.mx509UsernameField = x509usernamefield.get(1);
}
@@ -666,7 +667,7 @@ public class ConfigParser {
if (crlfile != null) {
// If the 'dir' parameter is present just add it as custom option ..
if (crlfile.size() == 3 && crlfile.get(2).equals("dir"))
- np.mCustomConfigOptions += TextUtils.join(" ", crlfile) + "\n";
+ np.mCustomConfigOptions += join(" ", crlfile) + "\n";
else
// Save the filename for the config converter to add later
np.mCrlFilename = crlfile.get(1);
@@ -732,6 +733,13 @@ public class ConfigParser {
return np;
}
+ private String join(String s, Vector<String> str) {
+ if (Build.VERSION.SDK_INT > 26)
+ return String.join(s, str);
+ else
+ return TextUtils.join(s, str);
+ }
+
private Pair<Connection, Connection[]> parseConnection(String connection, Connection defaultValues) throws IOException, ConfigParseError {
// Parse a connection Block as a new configuration file
@@ -783,20 +791,48 @@ public class ConfigParser {
}
}
- // Parse remote config
- Vector<Vector<String>> remotes = getAllOption("remote", 1, 3);
+ Vector<String> proxy = getOption("socks-proxy", 1, 2);
+ if (proxy == null)
+ proxy = getOption("http-proxy", 2, 2);
+
+ if (proxy != null) {
+ if (proxy.get(0).equals("socks-proxy")) {
+ conn.mProxyType = Connection.ProxyType.SOCKS5;
+ // socks defaults to 1080, http always sets port
+ conn.mProxyPort = "1080";
+ } else {
+ conn.mProxyType = Connection.ProxyType.HTTP;
+ }
+
+ conn.mProxyName = proxy.get(1);
+ if (proxy.size() >= 3)
+ conn.mProxyPort = proxy.get(2);
+ }
+ Vector<String> httpproxyauthhttp = getOption("http-proxy-user-pass", 1, 1);
+ if (httpproxyauthhttp != null)
+ useEmbbedHttpAuth(conn, httpproxyauthhttp.get(1));
- // Assume that we need custom options if connectionDefault are set
- if (connDefault != null) {
- for (Vector<Vector<String>> option : options.values()) {
- conn.mCustomConfiguration += getOptionStrings(option);
+ // 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());
+ optionsToRemove.add(option.getKey());
}
- if (!TextUtils.isEmpty(conn.mCustomConfiguration))
- conn.mUseCustomConfig = true;
}
+ for (String o: optionsToRemove)
+ options.remove(o);
+
+ if (!(conn.mCustomConfiguration == null || "".equals(conn.mCustomConfiguration.trim())))
+ conn.mUseCustomConfig = true;
+
// Make remotes empty to simplify code
if (remotes == null)
remotes = new Vector<Vector<String>>();
@@ -821,6 +857,7 @@ public class ConfigParser {
}
i++;
}
+
return Pair.create(conn, connections);
}
@@ -830,19 +867,19 @@ public class ConfigParser {
boolean noIpv4 = false;
if (defaultRoute)
- for (Vector<String> redirect : defgw)
- for (int i = 1; i < redirect.size(); i++) {
- if (redirect.get(i).equals("block-local"))
- np.mAllowLocalLAN = false;
- else if (redirect.get(i).equals("unblock-local"))
- np.mAllowLocalLAN = true;
- else if (redirect.get(i).equals("!ipv4"))
- noIpv4=true;
- else if (redirect.get(i).equals("ipv6"))
- np.mUseDefaultRoutev6=true;
- }
+ for (Vector<String> redirect : defgw)
+ for (int i = 1; i < redirect.size(); i++) {
+ if (redirect.get(i).equals("block-local"))
+ np.mAllowLocalLAN = false;
+ else if (redirect.get(i).equals("unblock-local"))
+ np.mAllowLocalLAN = true;
+ else if (redirect.get(i).equals("!ipv4"))
+ noIpv4 = true;
+ else if (redirect.get(i).equals("ipv6"))
+ np.mUseDefaultRoutev6 = true;
+ }
if (defaultRoute && !noIpv4)
- np.mUseDefaultRoute=true;
+ np.mUseDefaultRoute = true;
}
private boolean isUdpProto(String proto) throws ConfigParseError {
@@ -861,15 +898,6 @@ public class ConfigParser {
return isudp;
}
- static public void useEmbbedUserAuth(VpnProfile np, String inlinedata) {
- String data = VpnProfile.getEmbeddedContent(inlinedata);
- String[] parts = data.split("\n");
- if (parts.length >= 2) {
- np.mUsername = parts[0];
- np.mPassword = parts[1];
- }
- }
-
private void checkIgnoreAndInvalidOptions(VpnProfile np) throws ConfigParseError {
for (String option : unsupportedOptions)
if (options.containsKey(option))
@@ -880,7 +908,16 @@ public class ConfigParser {
options.remove(option);
- if (options.size() > 0) {
+ boolean customOptions=false;
+ for (Vector<Vector<String>> option: options.values())
+ {
+ for (Vector<String> optionsline : option) {
+ if (!ignoreThisOption(optionsline)) {
+ customOptions = true;
+ }
+ }
+ }
+ if (customOptions) {
np.mCustomConfigOptions = "# These options found in the config file do not map to config settings:\n"
+ np.mCustomConfigOptions;
@@ -894,7 +931,6 @@ public class ConfigParser {
}
}
-
boolean ignoreThisOption(Vector<String> option) {
for (String[] ignoreOption : ignoreOptionsWithArg) {
@@ -920,7 +956,7 @@ public class ConfigParser {
if (!ignoreThisOption(optionsline)) {
// Check if option had been inlined and inline again
if (optionsline.size() == 2 &&
- ("extra-certs".equals(optionsline.get(0)) || "http-proxy-user-pass".equals(optionsline.get(0)))) {
+ "extra-certs".equals(optionsline.get(0))) {
custom += VpnProfile.insertFileData(optionsline.get(0), optionsline.get(1));
} else {
for (String arg : optionsline)
@@ -932,7 +968,6 @@ public class ConfigParser {
return custom;
}
-
private void fixup(VpnProfile np) {
if (np.mRemoteCN.equals(np.mServerName)) {
np.mRemoteCN = "";
@@ -947,7 +982,6 @@ public class ConfigParser {
return alloptions.lastElement();
}
-
private Vector<Vector<String>> getAllOption(String option, int minarg, int maxarg) throws ConfigParseError {
Vector<Vector<String>> args = options.get(option);
if (args == null)
@@ -964,6 +998,19 @@ public class ConfigParser {
return args;
}
+ enum linestate {
+ initial,
+ readin_single_quote, reading_quoted, reading_unquoted, done
+ }
+
+ public static class ConfigParseError extends Exception {
+ private static final long serialVersionUID = -60L;
+
+ public ConfigParseError(String msg) {
+ super(msg);
+ }
+ }
+
}