From c95a21a736fadb46685e051064b0ec1efdae667a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Sat, 31 Jan 2015 01:19:49 +0100 Subject: Updated ics-openvpn to rev 1020. Improved build.gradle script. --- .../java/de/blinkt/openvpn/core/ConfigParser.java | 1030 ++++++++++---------- .../java/de/blinkt/openvpn/core/NetworkSpace.java | 13 +- .../de/blinkt/openvpn/core/OpenVPNService.java | 34 +- .../main/res/drawable-hdpi/ic_check_white_24dp.png | Bin 0 -> 309 bytes .../main/res/drawable-mdpi/ic_check_white_24dp.png | Bin 0 -> 243 bytes .../res/drawable-xhdpi/ic_check_white_24dp.png | Bin 0 -> 363 bytes .../res/drawable-xxhdpi/ic_check_white_24dp.png | Bin 0 -> 460 bytes .../res/drawable-xxxhdpi/ic_check_white_24dp.png | Bin 0 -> 587 bytes app/src/main/res/layout/log_fragment.xml | 1 + app/src/main/res/values-v21/refs.xml | 3 +- app/src/main/res/values/strings-icsopenvpn.xml | 1 + 11 files changed, 546 insertions(+), 536 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_check_white_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_check_white_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_check_white_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png (limited to 'app/src') 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 5dc96bbc..5f5d486c 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java +++ b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java @@ -28,11 +28,11 @@ import de.blinkt.openvpn.VpnProfile; public class ConfigParser { - public static final String CONVERTED_PROFILE = "converted Profile"; - private HashMap>> options = new HashMap>>(); - private HashMap> meta = new HashMap>(); + public static final String CONVERTED_PROFILE = "converted Profile"; + private HashMap>> options = new HashMap>>(); + private HashMap> meta = new HashMap>(); - public void parseConfig(Reader reader) throws IOException, ConfigParseError { + public void parseConfig(Reader reader) throws IOException, ConfigParseError { BufferedReader br = new BufferedReader(reader); @@ -75,192 +75,176 @@ public class ConfigParser { } catch (java.lang.OutOfMemoryError memoryError) { throw new ConfigParseError("File too large to parse: " + memoryError.getLocalizedMessage()); } - } + } - private Vector parsemeta(String line) { - String meta = line.split("#\\sOVPN_ACCESS_SERVER_", 2)[1]; - String[] parts = meta.split("=",2); - Vector rval = new Vector(); + private Vector parsemeta(String line) { + String meta = line.split("#\\sOVPN_ACCESS_SERVER_", 2)[1]; + String[] parts = meta.split("=", 2); + Vector rval = new Vector(); Collections.addAll(rval, parts); - return rval; - - } - - private void checkinlinefile(Vector args, BufferedReader br) throws IOException, ConfigParseError { - String arg0 = args.get(0).trim(); - // CHeck for - if(arg0.startsWith("<") && arg0.endsWith(">")) { - String argname = arg0.substring(1, arg0.length()-1); - String inlinefile = VpnProfile.INLINE_TAG; - - String endtag = String.format("",argname); - do { - String line = br.readLine(); - if(line==null){ - throw new ConfigParseError(String.format("No endtag for starttag <%s> found",argname,argname)); - } - if(line.trim().equals(endtag)) - break; - else { - inlinefile+=line; - inlinefile+= "\n"; - } - } while(true); - - args.clear(); - args.add(argname); - args.add(inlinefile); - } - - } - - 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: - return Character.isWhitespace(c) || c == '\0'; - - } - - public 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 parseline(String line) throws ConfigParseError { - Vector parameters = new Vector(); - - if (line.length()==0) - return parameters; - - - linestate state = linestate.initial; - boolean backslash = false; - char out=0; - - int pos=0; - String currentarg=""; - - do { - // Emulate the c parsing ... - char in; - if(pos < line.length()) - in = line.charAt(pos); - else - in = '\0'; - - if (!backslash && in == '\\' && state != linestate.readin_single_quote) - { - backslash = true; - } - else - { - if (state == linestate.initial) - { - if (!space (in)) - { - if (in == ';' || in == '#') /* comment */ - break; - if (!backslash && in == '\"') - state = linestate.reading_quoted; - else if (!backslash && in == '\'') - state = linestate.readin_single_quote; - else - { - out = in; - state = linestate.reading_unquoted; - } - } - } - else if (state == linestate.reading_unquoted) - { - if (!backslash && space (in)) - state = linestate.done; - else - out = in; - } - else if (state == linestate.reading_quoted) - { - if (!backslash && in == '\"') - state = linestate.done; - else - out = in; - } - else if (state == linestate.readin_single_quote) - { - if (in == '\'') - state = linestate.done; - else - out = in; - } - - if (state == linestate.done) - { - /* ASSERT (parm_len > 0); */ - state = linestate.initial; - parameters.add(currentarg); - currentarg = ""; - out =0; - } - - if (backslash && out!=0) - { - if (!(out == '\\' || out == '\"' || space (out))) - { - throw new ConfigParseError("Options warning: Bad backslash ('\\') usage"); - } - } - backslash = false; - } + return rval; + + } + + private void checkinlinefile(Vector args, BufferedReader br) throws IOException, ConfigParseError { + String arg0 = args.get(0).trim(); + // CHeck for + if (arg0.startsWith("<") && arg0.endsWith(">")) { + String argname = arg0.substring(1, arg0.length() - 1); + String inlinefile = VpnProfile.INLINE_TAG; + + String endtag = String.format("", argname); + do { + String line = br.readLine(); + if (line == null) { + throw new ConfigParseError(String.format("No endtag for starttag <%s> found", argname, argname)); + } + if (line.trim().equals(endtag)) + break; + else { + inlinefile += line; + inlinefile += "\n"; + } + } while (true); + + args.clear(); + args.add(argname); + args.add(inlinefile); + } + + } + + 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: + return Character.isWhitespace(c) || c == '\0'; + + } + + public 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 parseline(String line) throws ConfigParseError { + Vector parameters = new Vector(); + + if (line.length() == 0) + return parameters; + + + linestate state = linestate.initial; + boolean backslash = false; + char out = 0; + + int pos = 0; + String currentarg = ""; + + do { + // Emulate the c parsing ... + char in; + if (pos < line.length()) + in = line.charAt(pos); + else + in = '\0'; + + if (!backslash && in == '\\' && state != linestate.readin_single_quote) { + backslash = true; + } else { + if (state == linestate.initial) { + if (!space(in)) { + if (in == ';' || in == '#') /* comment */ + break; + if (!backslash && in == '\"') + state = linestate.reading_quoted; + else if (!backslash && in == '\'') + state = linestate.readin_single_quote; + else { + out = in; + state = linestate.reading_unquoted; + } + } + } else if (state == linestate.reading_unquoted) { + if (!backslash && space(in)) + state = linestate.done; + else + out = in; + } else if (state == linestate.reading_quoted) { + if (!backslash && in == '\"') + state = linestate.done; + else + out = in; + } else if (state == linestate.readin_single_quote) { + if (in == '\'') + state = linestate.done; + else + out = in; + } + + if (state == linestate.done) { + /* ASSERT (parm_len > 0); */ + state = linestate.initial; + parameters.add(currentarg); + currentarg = ""; + out = 0; + } + + if (backslash && out != 0) { + if (!(out == '\\' || out == '\"' || space(out))) { + throw new ConfigParseError("Options warning: Bad backslash ('\\') usage"); + } + } + backslash = false; + } /* store parameter character */ - if (out!=0) - { - currentarg+=out; - } - } while (pos++ < line.length()); - - 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", - "dhcp-release", - "dhcp-renew", - "dh", + if (out != 0) { + currentarg += out; + } + } while (pos++ < line.length()); + + 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", + "dhcp-release", + "dhcp-renew", + "dh", "group", "ip-win32", "management-hold", "management", "management-client", "management-query-remote", - "management-query-passwords", + "management-query-passwords", "management-query-proxy", "management-external-key", "management-forget-disconnect", @@ -269,32 +253,32 @@ public class ConfigParser { "management-up-down", "management-client-user", "management-client-group", - "pause-exit", + "pause-exit", "plugin", "machine-readable-output", - "persist-key", - "register-dns", - "route-delay", - "route-gateway", - "route-metric", - "route-method", - "status", - "script-security", - "show-net-up", - "suppress-timestamps", - "tmp-dir", - "tun-ipv6", - "topology", + "persist-key", + "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"} - }; + { + {"setenv", "IV_GUI_VER"}, + {"setenv", "IV_OPENVPN_GUI_VERSION"}, + {"engine", "dynamic"} + }; final String[] connectionOptions = { "local", @@ -326,70 +310,67 @@ public class ConfigParser { // This method is far too long - @SuppressWarnings("ConstantConditions") + @SuppressWarnings("ConstantConditions") public VpnProfile convertProfile() throws ConfigParseError, IOException { - boolean noauthtypeset=true; - VpnProfile np = new VpnProfile(CONVERTED_PROFILE); - // Pull, client, tls-client - np.clearDefaults(); - - if(options.containsKey("client") || options.containsKey("pull")) { - np.mUsePull=true; - options.remove("pull"); - options.remove("client"); - } - - Vector secret = getOption("secret", 1, 2); - if(secret!=null) - { - np.mAuthenticationType=VpnProfile.TYPE_STATICKEYS; - noauthtypeset=false; - np.mUseTLSAuth=true; - np.mTLSAuthFilename=secret.get(1); - if(secret.size()==3) - np.mTLSAuthDirection=secret.get(2); - - } - - Vector> routes = getAllOption("route", 1, 4); - if(routes!=null) { - String routeopt = ""; + boolean noauthtypeset = true; + VpnProfile np = new VpnProfile(CONVERTED_PROFILE); + // Pull, client, tls-client + np.clearDefaults(); + + if (options.containsKey("client") || options.containsKey("pull")) { + np.mUsePull = true; + options.remove("pull"); + options.remove("client"); + } + + Vector secret = getOption("secret", 1, 2); + if (secret != null) { + np.mAuthenticationType = VpnProfile.TYPE_STATICKEYS; + noauthtypeset = false; + np.mUseTLSAuth = true; + np.mTLSAuthFilename = secret.get(1); + if (secret.size() == 3) + np.mTLSAuthDirection = secret.get(2); + + } + + Vector> routes = getAllOption("route", 1, 4); + if (routes != null) { + String routeopt = ""; String routeExcluded = ""; - for(Vector route:routes){ - String netmask = "255.255.255.255"; + for (Vector route : routes) { + String netmask = "255.255.255.255"; String gateway = "vpn_gateway"; - if(route.size() >= 3) - netmask = route.get(2); + if (route.size() >= 3) + netmask = route.get(2); if (route.size() >= 4) gateway = route.get(3); - String net = route.get(1); - try { - CIDRIP cidr = new CIDRIP(net, netmask); + String net = route.get(1); + try { + CIDRIP cidr = new CIDRIP(net, netmask); if (gateway.equals("net_gateway")) routeExcluded += cidr.toString() + " "; else - routeopt+=cidr.toString() + " "; - } catch (ArrayIndexOutOfBoundsException aioob) { - throw new ConfigParseError("Could not parse netmask of route " + netmask); - } catch (NumberFormatException ne) { - - + routeopt += cidr.toString() + " "; + } catch (ArrayIndexOutOfBoundsException aioob) { + throw new ConfigParseError("Could not parse netmask of route " + netmask); + } catch (NumberFormatException ne) { - throw new ConfigParseError("Could not parse netmask of route " + netmask); - } + throw new ConfigParseError("Could not parse netmask of route " + netmask); + } - } - np.mCustomRoutes=routeopt; - np.mExcludedRoutes=routeExcluded; - } + } + np.mCustomRoutes = routeopt; + np.mExcludedRoutes = routeExcluded; + } Vector> routesV6 = getAllOption("route-ipv6", 1, 4); - if (routesV6!=null) { + if (routesV6 != null) { String customIPv6Routes = ""; - for (Vector route:routesV6){ + for (Vector route : routesV6) { customIPv6Routes += route.get(1) + " "; } @@ -397,39 +378,36 @@ public class ConfigParser { } // Also recognize tls-auth [inline] direction ... - Vector> tlsauthoptions = getAllOption("tls-auth", 1, 2); - if(tlsauthoptions!=null) { - for(Vector tlsauth:tlsauthoptions) { - if(tlsauth!=null) - { - if(!tlsauth.get(1).equals("[inline]")) { - np.mTLSAuthFilename=tlsauth.get(1); - np.mUseTLSAuth=true; - } - if(tlsauth.size()==3) - np.mTLSAuthDirection=tlsauth.get(2); - } - } - } - - Vector direction = getOption("key-direction", 1, 1); - if(direction!=null) - np.mTLSAuthDirection=direction.get(1); + Vector> tlsauthoptions = getAllOption("tls-auth", 1, 2); + if (tlsauthoptions != null) { + for (Vector tlsauth : tlsauthoptions) { + if (tlsauth != null) { + if (!tlsauth.get(1).equals("[inline]")) { + np.mTLSAuthFilename = tlsauth.get(1); + np.mUseTLSAuth = true; + } + if (tlsauth.size() == 3) + np.mTLSAuthDirection = tlsauth.get(2); + } + } + } + + Vector direction = getOption("key-direction", 1, 1); + if (direction != null) + np.mTLSAuthDirection = direction.get(1); Vector> defgw = getAllOption("redirect-gateway", 0, 5); - if(defgw != null) - { - np.mUseDefaultRoute=true; + if (defgw != null) { + np.mUseDefaultRoute = true; checkRedirectParameters(np, defgw); } - Vector> redirectPrivate = getAllOption("redirect-private",0,5); - if (redirectPrivate != null) - { - checkRedirectParameters(np,redirectPrivate); + Vector> redirectPrivate = getAllOption("redirect-private", 0, 5); + if (redirectPrivate != null) { + checkRedirectParameters(np, redirectPrivate); } - Vector dev =getOption("dev",1,1); - Vector devtype =getOption("dev-type",1,1); + Vector dev = getOption("dev", 1, 1); + Vector devtype = getOption("dev-type", 1, 1); if ((devtype != null && devtype.get(1).equals("tun")) || (dev != null && dev.get(1).startsWith("tun")) || @@ -437,15 +415,15 @@ public class ConfigParser { //everything okay } else { throw new ConfigParseError("Sorry. Only tun mode is supported. See the FAQ for more detail"); - } + } - Vector mssfix = getOption("mssfix",0,1); + Vector mssfix = getOption("mssfix", 0, 1); - if (mssfix!=null) { - if (mssfix.size()>=2) { + if (mssfix != null) { + if (mssfix.size() >= 2) { try { - np.mMssFix=Integer.parseInt(mssfix.get(1)); - } catch(NumberFormatException e) { + np.mMssFix = Integer.parseInt(mssfix.get(1)); + } catch (NumberFormatException e) { throw new ConfigParseError("Argument to --mssfix has to be an integer"); } } else { @@ -454,172 +432,171 @@ public class ConfigParser { } - Vector 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> dhcpoptions = getAllOption("dhcp-option", 2, 2); - if(dhcpoptions!=null) { - for(Vector dhcpoption:dhcpoptions) { - String type=dhcpoption.get(1); - String arg = dhcpoption.get(2); - if(type.equals("DOMAIN")) { - np.mSearchDomain=dhcpoption.get(2); - } else if(type.equals("DNS")) { - np.mOverrideDNS=true; - if(np.mDNS1.equals(VpnProfile.DEFAULT_DNS1)) - np.mDNS1=arg; - else - np.mDNS2=arg; - } - } - } - - Vector ifconfig = getOption("ifconfig", 2, 2); - if(ifconfig!=null) { - try { - CIDRIP cidr = new CIDRIP(ifconfig.get(1), ifconfig.get(2)); - np.mIPv4Address=cidr.toString(); - } catch (NumberFormatException nfe) { - throw new ConfigParseError("Could not pase ifconfig IP address: " + nfe.getLocalizedMessage()); - } - - } - - if(getOption("remote-random-hostname", 0, 0)!=null) - np.mUseRandomHostname=true; - - if(getOption("float", 0, 0)!=null) - np.mUseFloat=true; - - if(getOption("comp-lzo", 0, 1)!=null) - np.mUseLzo=true; - - Vector cipher = getOption("cipher", 1, 1); - if(cipher!=null) - np.mCipher= cipher.get(1); - - Vector auth = getOption("auth", 1, 1); - if(auth!=null) - np.mAuth = auth.get(1); - - - Vector ca = getOption("ca",1,1); - if(ca!=null){ - np.mCaFilename = ca.get(1); - } - - Vector cert = getOption("cert",1,1); - if(cert!=null){ - np.mClientCertFilename = cert.get(1); - np.mAuthenticationType = VpnProfile.TYPE_CERTIFICATES; - noauthtypeset=false; - } - Vector key= getOption("key",1,1); - if(key!=null) - np.mClientKeyFilename=key.get(1); - - Vector pkcs12 = getOption("pkcs12",1,1); - if(pkcs12!=null) { - np.mPKCS12Filename = pkcs12.get(1); - np.mAuthenticationType = VpnProfile.TYPE_KEYSTORE; - noauthtypeset=false; - } - - Vector cryptoapicert = getOption("cryptoapicert",1,1); - if(cryptoapicert!=null) { + Vector 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> dhcpoptions = getAllOption("dhcp-option", 2, 2); + if (dhcpoptions != null) { + for (Vector dhcpoption : dhcpoptions) { + String type = dhcpoption.get(1); + String arg = dhcpoption.get(2); + if (type.equals("DOMAIN")) { + np.mSearchDomain = dhcpoption.get(2); + } else if (type.equals("DNS")) { + np.mOverrideDNS = true; + if (np.mDNS1.equals(VpnProfile.DEFAULT_DNS1)) + np.mDNS1 = arg; + else + np.mDNS2 = arg; + } + } + } + + Vector ifconfig = getOption("ifconfig", 2, 2); + if (ifconfig != null) { + try { + CIDRIP cidr = new CIDRIP(ifconfig.get(1), ifconfig.get(2)); + np.mIPv4Address = cidr.toString(); + } catch (NumberFormatException nfe) { + throw new ConfigParseError("Could not pase ifconfig IP address: " + nfe.getLocalizedMessage()); + } + + } + + if (getOption("remote-random-hostname", 0, 0) != null) + np.mUseRandomHostname = true; + + if (getOption("float", 0, 0) != null) + np.mUseFloat = true; + + if (getOption("comp-lzo", 0, 1) != null) + np.mUseLzo = true; + + Vector cipher = getOption("cipher", 1, 1); + if (cipher != null) + np.mCipher = cipher.get(1); + + Vector auth = getOption("auth", 1, 1); + if (auth != null) + np.mAuth = auth.get(1); + + + Vector ca = getOption("ca", 1, 1); + if (ca != null) { + np.mCaFilename = ca.get(1); + } + + Vector cert = getOption("cert", 1, 1); + if (cert != null) { + np.mClientCertFilename = cert.get(1); + np.mAuthenticationType = VpnProfile.TYPE_CERTIFICATES; + noauthtypeset = false; + } + Vector key = getOption("key", 1, 1); + if (key != null) + np.mClientKeyFilename = key.get(1); + + Vector pkcs12 = getOption("pkcs12", 1, 1); + if (pkcs12 != null) { + np.mPKCS12Filename = pkcs12.get(1); np.mAuthenticationType = VpnProfile.TYPE_KEYSTORE; - noauthtypeset=false; + noauthtypeset = false; } - Vector compatnames = getOption("compat-names",1,2); - Vector nonameremapping = getOption("no-name-remapping",1,1); - Vector tlsremote = getOption("tls-remote",1,1); - if(tlsremote!=null){ - np.mRemoteCN = tlsremote.get(1); - np.mCheckRemoteCN=true; - np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE; + Vector cryptoapicert = getOption("cryptoapicert", 1, 1); + if (cryptoapicert != null) { + np.mAuthenticationType = VpnProfile.TYPE_KEYSTORE; + noauthtypeset = false; + } - if((compatnames!=null && compatnames.size() > 2) || - (nonameremapping!=null)) - np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING; - } + Vector compatnames = getOption("compat-names", 1, 2); + Vector nonameremapping = getOption("no-name-remapping", 1, 1); + Vector tlsremote = getOption("tls-remote", 1, 1); + if (tlsremote != null) { + np.mRemoteCN = tlsremote.get(1); + np.mCheckRemoteCN = true; + np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE; + + if ((compatnames != null && compatnames.size() > 2) || + (nonameremapping != null)) + np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING; + } - Vector verifyx509name = getOption("verify-x509-name",1,2); - if(verifyx509name!=null){ - np.mRemoteCN = verifyx509name.get(1); - np.mCheckRemoteCN=true; - if(verifyx509name.size()>2) { - if (verifyx509name.get(2).equals("name")) - np.mX509AuthType=VpnProfile.X509_VERIFY_TLSREMOTE_RDN; - else if (verifyx509name.get(2).equals("name-prefix")) - np.mX509AuthType=VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX; - else - throw new ConfigParseError("Unknown parameter to x509-verify-name: " + verifyx509name.get(2) ); - } else { - np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE_DN; - } + Vector verifyx509name = getOption("verify-x509-name", 1, 2); + if (verifyx509name != null) { + np.mRemoteCN = verifyx509name.get(1); + np.mCheckRemoteCN = true; + if (verifyx509name.size() > 2) { + if (verifyx509name.get(2).equals("name")) + np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE_RDN; + else if (verifyx509name.get(2).equals("name-prefix")) + np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX; + else + throw new ConfigParseError("Unknown parameter to x509-verify-name: " + verifyx509name.get(2)); + } else { + np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE_DN; + } - } + } - Vector verb = getOption("verb",1,1); - if(verb!=null){ - np.mVerb=verb.get(1); - } + Vector verb = getOption("verb", 1, 1); + if (verb != null) { + np.mVerb = verb.get(1); + } - if(getOption("nobind", 0, 0) != null) - np.mNobind=true; + if (getOption("nobind", 0, 0) != null) + np.mNobind = true; - if(getOption("persist-tun", 0,0) != null) - np.mPersistTun=true; + if (getOption("persist-tun", 0, 0) != null) + np.mPersistTun = true; - Vector connectretry = getOption("connect-retry", 1, 1); - if(connectretry!=null) - np.mConnectRetry =connectretry.get(1); + Vector connectretry = getOption("connect-retry", 1, 1); + if (connectretry != null) + np.mConnectRetry = connectretry.get(1); - Vector connectretrymax = getOption("connect-retry-max", 1, 1); - if(connectretrymax!=null) - np.mConnectRetryMax =connectretrymax.get(1); + Vector connectretrymax = getOption("connect-retry-max", 1, 1); + if (connectretrymax != null) + np.mConnectRetryMax = connectretrymax.get(1); Vector> remotetls = getAllOption("remote-cert-tls", 1, 1); - if(remotetls!=null) - if(remotetls.get(0).get(1).equals("server")) - np.mExpectTLSCert=true; - else - options.put("remotetls",remotetls); - - Vector authuser = getOption("auth-user-pass",0,1); - if(authuser !=null){ - if(noauthtypeset) { - np.mAuthenticationType=VpnProfile.TYPE_USERPASS; - } else if(np.mAuthenticationType==VpnProfile.TYPE_CERTIFICATES) { - np.mAuthenticationType=VpnProfile.TYPE_USERPASS_CERTIFICATES; - } else if(np.mAuthenticationType==VpnProfile.TYPE_KEYSTORE) { - np.mAuthenticationType=VpnProfile.TYPE_USERPASS_KEYSTORE; - } - if(authuser.size()>1) { - // Set option value to password get to embed later. - np.mUsername=null; - useEmbbedUserAuth(np, authuser.get(1)); - } - } + if (remotetls != null) + if (remotetls.get(0).get(1).equals("server")) + np.mExpectTLSCert = true; + else + options.put("remotetls", remotetls); + + Vector authuser = getOption("auth-user-pass", 0, 1); + if (authuser != null) { + if (noauthtypeset) { + np.mAuthenticationType = VpnProfile.TYPE_USERPASS; + } else if (np.mAuthenticationType == VpnProfile.TYPE_CERTIFICATES) { + np.mAuthenticationType = VpnProfile.TYPE_USERPASS_CERTIFICATES; + } else if (np.mAuthenticationType == VpnProfile.TYPE_KEYSTORE) { + np.mAuthenticationType = VpnProfile.TYPE_USERPASS_KEYSTORE; + } + if (authuser.size() > 1) { + // Set option value to password get to embed later. + np.mUsername = null; + useEmbbedUserAuth(np, authuser.get(1)); + } + } Pair conns = parseConnectionOptions(null); - np.mConnections =conns.second; + np.mConnections = conns.second; Vector> connectionBlocks = getAllOption("connection", 1, 1); - if (np.mConnections.length > 0 && connectionBlocks !=null ) { - throw new ConfigParseError("Using a block and --remote is not allowed."); + if (np.mConnections.length > 0 && connectionBlocks != null) { + throw new ConfigParseError("Using a block and --remote is not allowed."); } - if (connectionBlocks!=null) { + if (connectionBlocks != null) { np.mConnections = new Connection[connectionBlocks.size()]; int connIndex = 0; @@ -633,43 +610,43 @@ public class ConfigParser { connIndex++; } } - if(getOption("remote-random", 0, 0) != null) - np.mRemoteRandom=true; + if (getOption("remote-random", 0, 0) != null) + np.mRemoteRandom = true; Vector protoforce = getOption("proto-force", 1, 1); - if(protoforce!=null) { + if (protoforce != null) { boolean disableUDP; String protoToDisable = protoforce.get(1); if (protoToDisable.equals("udp")) - disableUDP=true; + disableUDP = true; else if (protoToDisable.equals("tcp")) - disableUDP=false; + disableUDP = false; else throw new ConfigParseError(String.format("Unknown protocol %s in proto-force", protoToDisable)); - for (Connection conn:np.mConnections) - if(conn.mUseUdp==disableUDP) - conn.mEnabled=false; + for (Connection conn : np.mConnections) + if (conn.mUseUdp == disableUDP) + conn.mEnabled = false; } // Parse OpenVPN Access Server extra - Vector friendlyname = meta.get("FRIENDLY_NAME"); - if(friendlyname !=null && friendlyname.size() > 1) - np.mName=friendlyname.get(1); + Vector friendlyname = meta.get("FRIENDLY_NAME"); + if (friendlyname != null && friendlyname.size() > 1) + np.mName = friendlyname.get(1); - Vector ocusername = meta.get("USERNAME"); - if(ocusername !=null && ocusername.size() > 1) - np.mUsername=ocusername.get(1); + Vector ocusername = meta.get("USERNAME"); + if (ocusername != null && ocusername.size() > 1) + np.mUsername = ocusername.get(1); checkIgnoreAndInvalidOptions(np); - fixup(np); + fixup(np); - return np; - } + return np; + } private Pair parseConnection(String connection, Connection defaultValues) throws IOException, ConfigParseError { - // Parse a connection Block as a new configuration file + // Parse a connection Block as a new configuration file ConfigParser connectionParser = new ConfigParser(); @@ -683,7 +660,7 @@ public class ConfigParser { private Pair parseConnectionOptions(Connection connDefault) throws ConfigParseError { Connection conn; - if (connDefault!=null) + if (connDefault != null) try { conn = connDefault.clone(); } catch (CloneNotSupportedException e) { @@ -693,28 +670,28 @@ public class ConfigParser { else conn = new Connection(); - Vector port = getOption("port", 1,1); - if(port!=null){ + Vector port = getOption("port", 1, 1); + if (port != null) { conn.mServerPort = port.get(1); } - Vector rport = getOption("rport", 1,1); - if(rport!=null){ + Vector rport = getOption("rport", 1, 1); + if (rport != null) { conn.mServerPort = rport.get(1); } - Vector proto = getOption("proto", 1,1); - if(proto!=null){ - conn.mUseUdp=isUdpProto(proto.get(1)); + Vector proto = getOption("proto", 1, 1); + if (proto != null) { + conn.mUseUdp = isUdpProto(proto.get(1)); } // Parse remote config - Vector> remotes = getAllOption("remote",1,3); + Vector> remotes = getAllOption("remote", 1, 3); // Assume that we need custom options if connectionDefault are set - if(connDefault!=null) { + if (connDefault != null) { for (Vector> option : options.values()) { conn.mCustomConfiguration += getOptionStrings(option); @@ -724,14 +701,14 @@ public class ConfigParser { conn.mUseCustomConfig = true; } // Make remotes empty to simplify code - if (remotes==null) + if (remotes == null) remotes = new Vector>(); Connection[] connections = new Connection[remotes.size()]; - int i=0; - for (Vector remote: remotes) { + int i = 0; + for (Vector remote : remotes) { try { connections[i] = conn.clone(); } catch (CloneNotSupportedException e) { @@ -739,7 +716,7 @@ public class ConfigParser { } switch (remote.size()) { case 4: - connections[i].mUseUdp=isUdpProto(remote.get(3)); + connections[i].mUseUdp = isUdpProto(remote.get(3)); case 3: connections[i].mServerPort = remote.get(2); case 2: @@ -752,63 +729,60 @@ public class ConfigParser { } private void checkRedirectParameters(VpnProfile np, Vector> defgw) { - for (Vector redirect: defgw) - for (int i=1;i redirect : defgw) + for (int i = 1; i < redirect.size(); i++) { if (redirect.get(i).equals("block-local")) - np.mAllowLocalLAN=false; + np.mAllowLocalLAN = false; else if (redirect.get(i).equals("unblock-local")) - np.mAllowLocalLAN=true; + np.mAllowLocalLAN = true; } } - private boolean isUdpProto(String proto) throws ConfigParseError { - boolean isudp; - if(proto.equals("udp") || proto.equals("udp6")) - isudp=true; - else if (proto.equals("tcp-client") || - proto.equals("tcp") || - proto.equals("tcp6") || - proto.endsWith("tcp6-client")) - isudp =false; - else - throw new ConfigParseError("Unsupported option to --proto " + proto); - 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)) - throw new ConfigParseError(String.format("Unsupported Option %s encountered in config file. Aborting",option)); + private boolean isUdpProto(String proto) throws ConfigParseError { + boolean isudp; + if (proto.equals("udp") || proto.equals("udp6")) + isudp = true; + else if (proto.equals("tcp-client") || + proto.equals("tcp") || + proto.equals("tcp6") || + proto.endsWith("tcp6-client")) + isudp = false; + else + throw new ConfigParseError("Unsupported option to --proto " + proto); + return isudp; + } - for(String option:ignoreOptions) - // removing an item which is not in the map is no error - options.remove(option); + 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)) + throw new ConfigParseError(String.format("Unsupported Option %s encountered in config file. Aborting", option)); + for (String option : ignoreOptions) + // removing an item which is not in the map is no error + options.remove(option); - if(options.size()> 0) { - np.mCustomConfigOptions += "# These Options were found in the config file do not map to config settings:\n"; + if (options.size() > 0) { + np.mCustomConfigOptions += "# These Options were found in the config file do not map to config settings:\n"; - for(Vector> option:options.values()) { + for (Vector> option : options.values()) { - np.mCustomConfigOptions += getOptionStrings(option); + np.mCustomConfigOptions += getOptionStrings(option); - } - np.mUseCustomConfig=true; + } + np.mUseCustomConfig = true; - } - } + } + } boolean ignoreThisOption(Vector option) { @@ -843,35 +817,35 @@ public class ConfigParser { private void fixup(VpnProfile np) { - if(np.mRemoteCN.equals(np.mServerName)) { - np.mRemoteCN=""; - } - } - - private Vector getOption(String option, int minarg, int maxarg) throws ConfigParseError { - Vector> alloptions = getAllOption(option, minarg, maxarg); - if(alloptions==null) - return null; - else - return alloptions.lastElement(); - } - - - private Vector> getAllOption(String option, int minarg, int maxarg) throws ConfigParseError { - Vector> args = options.get(option); - if(args==null) - return null; - - for(Vector optionline:args) - - if(optionline.size()< (minarg+1) || optionline.size() > maxarg+1) { - String err = String.format(Locale.getDefault(),"Option %s has %d parameters, expected between %d and %d", - option,optionline.size()-1,minarg,maxarg ); - throw new ConfigParseError(err); - } - options.remove(option); - return args; - } + if (np.mRemoteCN.equals(np.mServerName)) { + np.mRemoteCN = ""; + } + } + + private Vector getOption(String option, int minarg, int maxarg) throws ConfigParseError { + Vector> alloptions = getAllOption(option, minarg, maxarg); + if (alloptions == null) + return null; + else + return alloptions.lastElement(); + } + + + private Vector> getAllOption(String option, int minarg, int maxarg) throws ConfigParseError { + Vector> args = options.get(option); + if (args == null) + return null; + + for (Vector optionline : args) + + if (optionline.size() < (minarg + 1) || optionline.size() > maxarg + 1) { + String err = String.format(Locale.getDefault(), "Option %s has %d parameters, expected between %d and %d", + option, optionline.size() - 1, minarg, maxarg); + throw new ConfigParseError(err); + } + options.remove(option); + return args; + } } diff --git a/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java b/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java index 26354689..c86f9e44 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java +++ b/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java @@ -172,8 +172,16 @@ public class NetworkSpace { } public boolean containsNet(ipAddress network) { - return getFirstAddress().compareTo(network.getFirstAddress()) != 1 && - getLastAddress().compareTo(network.getLastAddress()) != -1; + // this.first >= net.first && this.last <= net.last + BigInteger ourFirst = getFirstAddress(); + BigInteger ourLast = getLastAddress(); + BigInteger netFirst = network.getFirstAddress(); + BigInteger netLast = network.getLastAddress(); + + boolean a = ourFirst.compareTo(netFirst) != 1; + boolean b = ourLast.compareTo(netLast) != -1; + return a && b; + } } @@ -320,6 +328,7 @@ public class NetworkSpace { boolean skipIp=false; // If there is any smaller net that is excluded we may not add the positive route back + for (ipAddress calculatedIp: ipsSorted) { if(!calculatedIp.included && origIp.containsNet(calculatedIp)) { skipIp=true; 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 3cb5527b..3c1ec064 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -51,6 +51,8 @@ import static de.blinkt.openvpn.core.NetworkSpace.ipAddress; import static de.blinkt.openvpn.core.VpnStatus.ConnectionStatus.LEVEL_CONNECTED; import static de.blinkt.openvpn.core.VpnStatus.ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET; import static de.blinkt.openvpn.core.VpnStatus.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT; +import static de.blinkt.openvpn.core.VpnStatus.ConnectionStatus.LEVEL_NOTCONNECTED; +import static de.blinkt.openvpn.core.VpnStatus.ConnectionStatus.LEVEL_NONETWORK; import se.leap.bitmaskclient.Dashboard; @@ -174,7 +176,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac mNotificationManager.notify(OPENVPN_STATUS, notification); - startForeground(OPENVPN_STATUS, notification); + //startForeground(OPENVPN_STATUS, notification); } private int getIconByConnectionStatus(ConnectionStatus level) { @@ -552,9 +554,15 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac Collection positiveIPv4Routes = mRoutes.getPositiveIPList(); Collection positiveIPv6Routes = mRoutesv6.getPositiveIPList(); + ipAddress multicastRange = new ipAddress(new CIDRIP("224.0.0.0", 3), true); + for (NetworkSpace.ipAddress route : positiveIPv4Routes) { try { - builder.addRoute(route.getIPv4Address(), route.networkMask); + + if (multicastRange.containsNet(route)) + VpnStatus.logDebug(R.string.ignore_multicast_route, route.toString()); + else + builder.addRoute(route.getIPv4Address(), route.networkMask); } catch (IllegalArgumentException ia) { VpnStatus.logError(getString(R.string.route_rejected) + route + " " + ia.getLocalizedMessage()); } @@ -607,7 +615,10 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac try { //Debug.stopMethodTracing(); - return builder.establish(); + ParcelFileDescriptor tun = builder.establish(); + if (tun==null) + throw new NullPointerException("Android establish() method returned null (Really broken network configuration?)"); + return tun; } catch (Exception e) { VpnStatus.logError(R.string.tun_open_error); VpnStatus.logError(getString(R.string.error) + e.getLocalizedMessage()); @@ -843,6 +854,21 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac mDisplayBytecount = true; mConnecttime = System.currentTimeMillis(); lowpriority = true; + if(mProfile.mPersistTun) { + NotificationManager ns = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + ns.cancel(OPENVPN_STATUS); + return; + } + } else if (level == LEVEL_NONETWORK || level == LEVEL_NOTCONNECTED) { + NotificationManager ns = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + ns.cancel(OPENVPN_STATUS); + return; + } else if (level != LEVEL_NOTCONNECTED && mConnecttime > 0) { + mDisplayBytecount = false; + String msg = "Traffic is blocked until the VPN becomes active."; + String ticker = msg; + showNotification(msg, ticker, lowpriority , 0, level); + return; } else { mDisplayBytecount = false; } @@ -876,7 +902,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac humanReadableByteCount(diffOut / OpenVPNManagement.mBytecountInterval, true)); boolean lowpriority = !mNotificationAlwaysVisible; - showNotification(netstat, null, lowpriority, mConnecttime, LEVEL_CONNECTED); + //showNotification(netstat, null, lowpriority, mConnecttime, LEVEL_CONNECTED); } } diff --git a/app/src/main/res/drawable-hdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_check_white_24dp.png new file mode 100644 index 00000000..f42a0e2d Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_check_white_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_check_white_24dp.png new file mode 100644 index 00000000..e91f9048 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_check_white_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_check_white_24dp.png new file mode 100644 index 00000000..e5024472 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_check_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png new file mode 100644 index 00000000..6e03d54c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png new file mode 100644 index 00000000..87892840 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png differ diff --git a/app/src/main/res/layout/log_fragment.xml b/app/src/main/res/layout/log_fragment.xml index 4fec942e..491882a9 100644 --- a/app/src/main/res/layout/log_fragment.xml +++ b/app/src/main/res/layout/log_fragment.xml @@ -13,6 +13,7 @@ diff --git a/app/src/main/res/values-v21/refs.xml b/app/src/main/res/values-v21/refs.xml index d29d04ed..a4a26bec 100644 --- a/app/src/main/res/values-v21/refs.xml +++ b/app/src/main/res/values-v21/refs.xml @@ -7,9 +7,8 @@ @drawable/ic_close_white_24dp @drawable/ic_share_white_24dp + @drawable/ic_check_white_24dp @drawable/ic_filter_list_white_24dp @drawable/ic_delete_white_24dp @drawable/ic_delete_grey600_24dp - - @drawable/ic_edit_white_24dp diff --git a/app/src/main/res/values/strings-icsopenvpn.xml b/app/src/main/res/values/strings-icsopenvpn.xml index 307d3a42..39ad1193 100755 --- a/app/src/main/res/values/strings-icsopenvpn.xml +++ b/app/src/main/res/values/strings-icsopenvpn.xml @@ -350,5 +350,6 @@ Show log Multiple OpenVPN clients for Android exist. The most common ones are OpenVPN for Android (this client), OpenVPN Connect and OpenVPN Settings.<p>The clients can be grouped into two groups: OpenVPN for Android and OpenVPN Connect use the official VPNService API (Android 4.0+) and require no root and OpenVPN Settings which uses root.<p>OpenVPN for Android is an open source client and developed by Arne Schwabe. It is targeted at more advanced users and offers many settings and the ability to import profiles from files and to configure/change profiles inside the app. The client is based on the community version of OpenVPN. It is based on the OpenVPN 2.x source code. This client can be seen as the semi officially client of the community. <p>OpenVPN Connect is non open source client that is developed by OpenVPN Technologies, Inc. The client is indented to be general use client and moree targeted at the average user and allows the import of OpenVPN profiles. This client is based on the OpenVPN C++ reimplementation of the OpenVPN protocol (This was required to allow OpenVPN Technologies, Inc to publish an iOS OpenVPN app). This client is the official client of the OpenVPN technologies <p> OpenVPN Settings is the oldest of the clients and also a UI for the open source OpenVPN. In contrast to OpenVPN for Android it requires root and does not use the VPNService API. It does not depend on Android 4.0+ Differences between the OpenVPN Android clients + Ignoring multicast route: %s -- cgit v1.2.3