diff options
Diffstat (limited to 'app/src/main')
18 files changed, 607 insertions, 747 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 33c3eaba..e11b57ef 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,8 +17,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="se.leap.bitmaskclient" - android:versionCode="112" - android:versionName="0.9.1" > + android:versionCode="116" + android:versionName="0.9.2" > <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> diff --git a/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl b/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl deleted file mode 100644 index 2a492f78..00000000 --- a/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.vending.billing; - -import android.os.Bundle; - -/** - * InAppBillingService is the service that provides in-app billing version 3 and beyond. - * This service provides the following features: - * 1. Provides a new API to get details of in-app items published for the app including - * price, type, title and description. - * 2. The purchase flow is synchronous and purchase information is available immediately - * after it completes. - * 3. Purchase information of in-app purchases is maintained within the Google Play system - * till the purchase is consumed. - * 4. An API to consume a purchase of an inapp item. All purchases of one-time - * in-app items are consumable and thereafter can be purchased again. - * 5. An API to get current purchases of the user immediately. This will not contain any - * consumed purchases. - * - * All calls will give a response code with the following possible values - * RESULT_OK = 0 - success - * RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog - * RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested - * RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase - * RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API - * RESULT_ERROR = 6 - Fatal error during the API action - * RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned - * RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned - */ -interface IInAppBillingService { - /** - * Checks support for the requested billing API version, package and in-app type. - * Minimum API version supported by this interface is 3. - * @param apiVersion the billing version which the app is using - * @param packageName the package name of the calling app - * @param type type of the in-app item being purchased "inapp" for one-time purchases - * and "subs" for subscription. - * @return RESULT_OK(0) on success, corresponding result code on failures - */ - int isBillingSupported(int apiVersion, String packageName, String type); - - /** - * Provides details of a list of SKUs - * Given a list of SKUs of a valid type in the skusBundle, this returns a bundle - * with a list JSON strings containing the productId, price, title and description. - * This API can be called with a maximum of 20 SKUs. - * @param apiVersion billing API version that the Third-party is using - * @param packageName the package name of the calling app - * @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST" - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "DETAILS_LIST" with a StringArrayList containing purchase information - * in JSON format similar to: - * '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00", - * "title : "Example Title", "description" : "This is an example description" }' - */ - Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle); - - /** - * Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU, - * the type, a unique purchase token and an optional developer payload. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param sku the SKU of the in-app item as published in the developer console - * @param type the type of the in-app item ("inapp" for one-time purchases - * and "subs" for subscription). - * @param developerPayload optional argument to be sent back with the purchase information - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "BUY_INTENT" - PendingIntent to start the purchase flow - * - * The Pending intent should be launched with startIntentSenderForResult. When purchase flow - * has completed, the onActivityResult() will give a resultCode of OK or CANCELED. - * If the purchase is successful, the result data will contain the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "INAPP_PURCHASE_DATA" - String in JSON format similar to - * '{"orderId":"12999763169054705758.1371079406387615", - * "packageName":"com.example.app", - * "productId":"exampleSku", - * "purchaseTime":1345678900000, - * "purchaseToken" : "122333444455555", - * "developerPayload":"example developer payload" }' - * "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that - * was signed with the private key of the developer - * TODO: change this to app-specific keys. - */ - Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type, - String developerPayload); - - /** - * Returns the current SKUs owned by the user of the type and package name specified along with - * purchase information and a signature of the data to be validated. - * This will return all SKUs that have been purchased in V3 and managed items purchased using - * V1 and V2 that have not been consumed. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param type the type of the in-app items being requested - * ("inapp" for one-time purchases and "subs" for subscription). - * @param continuationToken to be set as null for the first call, if the number of owned - * skus are too many, a continuationToken is returned in the response bundle. - * This method can be called again with the continuation token to get the next set of - * owned skus. - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs - * "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information - * "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures - * of the purchase information - * "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the - * next set of in-app purchases. Only set if the - * user has more owned skus than the current list. - */ - Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken); - - /** - * Consume the last purchase of the given SKU. This will result in this item being removed - * from all subsequent responses to getPurchases() and allow re-purchase of this item. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param purchaseToken token in the purchase information JSON that identifies the purchase - * to be consumed - * @return 0 if consumption succeeded. Appropriate error values for failures. - */ - int consumePurchase(int apiVersion, String packageName, String purchaseToken); -} diff --git a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java index 02abd7a1..0eb1d99c 100644 --- a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java +++ b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java @@ -120,23 +120,23 @@ public class LaunchVPN extends Activity { @Override protected void onActivityResult (int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - if(requestCode==START_VPN_PROFILE) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - boolean showLogWindow = prefs.getBoolean("showlogwindow", true); - - if(!mhideLog && showLogWindow) - showLogWindow(); - new startOpenVpnThread().start(); - } else if (resultCode == Activity.RESULT_CANCELED) { - // User does not want us to start, so we just vanish - VpnStatus.updateStateString("USER_VPN_PERMISSION_CANCELLED", "", R.string.state_user_vpn_permission_cancelled, - ConnectionStatus.LEVEL_NOTCONNECTED); - - finish(); - } + super.onActivityResult(requestCode, resultCode, data); + + if(requestCode==START_VPN_PROFILE) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + boolean showlogwindow = prefs.getBoolean("showlogwindow", true); + + if(!mhideLog && showlogwindow) + showLogWindow(); + new startOpenVpnThread().start(); + } else if (resultCode == Activity.RESULT_CANCELED) { + // User does not want us to start, so we just vanish + VpnStatus.updateStateString("USER_VPN_PERMISSION_CANCELLED", "", R.string.state_user_vpn_permission_cancelled, ConnectionStatus.LEVEL_NOTCONNECTED); + + finish(); + } } + void showLogWindow() { Intent startLW = new Intent(getBaseContext(),LogWindow.class); 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<String, Vector<Vector<String>>> options = new HashMap<String, Vector<Vector<String>>>(); - private HashMap<String, Vector<String>> meta = new HashMap<String, Vector<String>>(); + public static final String CONVERTED_PROFILE = "converted Profile"; + private HashMap<String, Vector<Vector<String>>> options = new HashMap<String, Vector<Vector<String>>>(); + private HashMap<String, Vector<String>> meta = new HashMap<String, Vector<String>>(); - 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<String> parsemeta(String line) { - String meta = line.split("#\\sOVPN_ACCESS_SERVER_", 2)[1]; - String[] parts = meta.split("=",2); - Vector<String> rval = new Vector<String>(); + private Vector<String> parsemeta(String line) { + String meta = line.split("#\\sOVPN_ACCESS_SERVER_", 2)[1]; + String[] parts = meta.split("=", 2); + Vector<String> rval = new Vector<String>(); Collections.addAll(rval, parts); - return rval; - - } - - private void checkinlinefile(Vector<String> args, BufferedReader br) throws IOException, ConfigParseError { - String arg0 = args.get(0).trim(); - // CHeck for <foo> - if(arg0.startsWith("<") && arg0.endsWith(">")) { - String argname = arg0.substring(1, arg0.length()-1); - String inlinefile = VpnProfile.INLINE_TAG; - - String endtag = String.format("</%s>",argname); - do { - String line = br.readLine(); - if(line==null){ - throw new ConfigParseError(String.format("No endtag </%s> 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<String> parseline(String line) throws ConfigParseError { - Vector<String> parameters = new Vector<String>(); - - 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<String> args, BufferedReader br) throws IOException, ConfigParseError { + String arg0 = args.get(0).trim(); + // CHeck for <foo> + if (arg0.startsWith("<") && arg0.endsWith(">")) { + String argname = arg0.substring(1, arg0.length() - 1); + String inlinefile = VpnProfile.INLINE_TAG; + + String endtag = String.format("</%s>", argname); + do { + String line = br.readLine(); + if (line == null) { + throw new ConfigParseError(String.format("No endtag </%s> 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<String> parseline(String line) throws ConfigParseError { + Vector<String> parameters = new Vector<String>(); + + 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<String> 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<Vector<String>> 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<String> 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<Vector<String>> routes = getAllOption("route", 1, 4); + if (routes != null) { + String routeopt = ""; String routeExcluded = ""; - for(Vector<String> route:routes){ - String netmask = "255.255.255.255"; + for (Vector<String> 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<Vector<String>> routesV6 = getAllOption("route-ipv6", 1, 4); - if (routesV6!=null) { + if (routesV6 != null) { String customIPv6Routes = ""; - for (Vector<String> route:routesV6){ + for (Vector<String> route : routesV6) { customIPv6Routes += route.get(1) + " "; } @@ -397,39 +378,36 @@ public class ConfigParser { } // Also recognize tls-auth [inline] direction ... - Vector<Vector<String>> tlsauthoptions = getAllOption("tls-auth", 1, 2); - if(tlsauthoptions!=null) { - for(Vector<String> 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<String> direction = getOption("key-direction", 1, 1); - if(direction!=null) - np.mTLSAuthDirection=direction.get(1); + Vector<Vector<String>> tlsauthoptions = getAllOption("tls-auth", 1, 2); + if (tlsauthoptions != null) { + for (Vector<String> 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<String> direction = getOption("key-direction", 1, 1); + if (direction != null) + np.mTLSAuthDirection = direction.get(1); Vector<Vector<String>> defgw = getAllOption("redirect-gateway", 0, 5); - if(defgw != null) - { - np.mUseDefaultRoute=true; + if (defgw != null) { + np.mUseDefaultRoute = true; checkRedirectParameters(np, defgw); } - Vector<Vector<String>> redirectPrivate = getAllOption("redirect-private",0,5); - if (redirectPrivate != null) - { - checkRedirectParameters(np,redirectPrivate); + Vector<Vector<String>> redirectPrivate = getAllOption("redirect-private", 0, 5); + if (redirectPrivate != null) { + checkRedirectParameters(np, redirectPrivate); } - Vector<String> dev =getOption("dev",1,1); - Vector<String> devtype =getOption("dev-type",1,1); + Vector<String> dev = getOption("dev", 1, 1); + Vector<String> 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<String> mssfix = getOption("mssfix",0,1); + Vector<String> 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<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) { - 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<String> 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<String> cipher = getOption("cipher", 1, 1); - if(cipher!=null) - np.mCipher= cipher.get(1); - - Vector<String> auth = getOption("auth", 1, 1); - if(auth!=null) - np.mAuth = auth.get(1); - - - Vector<String> ca = getOption("ca",1,1); - if(ca!=null){ - np.mCaFilename = ca.get(1); - } - - Vector<String> cert = getOption("cert",1,1); - if(cert!=null){ - np.mClientCertFilename = cert.get(1); - np.mAuthenticationType = VpnProfile.TYPE_CERTIFICATES; - noauthtypeset=false; - } - Vector<String> key= getOption("key",1,1); - if(key!=null) - np.mClientKeyFilename=key.get(1); - - Vector<String> pkcs12 = getOption("pkcs12",1,1); - if(pkcs12!=null) { - np.mPKCS12Filename = pkcs12.get(1); - np.mAuthenticationType = VpnProfile.TYPE_KEYSTORE; - noauthtypeset=false; - } - - Vector<String> cryptoapicert = getOption("cryptoapicert",1,1); - if(cryptoapicert!=null) { + 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) { + 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<String> 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<String> cipher = getOption("cipher", 1, 1); + if (cipher != null) + np.mCipher = cipher.get(1); + + Vector<String> auth = getOption("auth", 1, 1); + if (auth != null) + np.mAuth = auth.get(1); + + + Vector<String> ca = getOption("ca", 1, 1); + if (ca != null) { + np.mCaFilename = ca.get(1); + } + + Vector<String> cert = getOption("cert", 1, 1); + if (cert != null) { + np.mClientCertFilename = cert.get(1); + np.mAuthenticationType = VpnProfile.TYPE_CERTIFICATES; + noauthtypeset = false; + } + Vector<String> key = getOption("key", 1, 1); + if (key != null) + np.mClientKeyFilename = key.get(1); + + Vector<String> pkcs12 = getOption("pkcs12", 1, 1); + if (pkcs12 != null) { + np.mPKCS12Filename = pkcs12.get(1); np.mAuthenticationType = VpnProfile.TYPE_KEYSTORE; - noauthtypeset=false; + noauthtypeset = false; } - Vector<String> compatnames = getOption("compat-names",1,2); - Vector<String> nonameremapping = getOption("no-name-remapping",1,1); - Vector<String> tlsremote = getOption("tls-remote",1,1); - if(tlsremote!=null){ - np.mRemoteCN = tlsremote.get(1); - np.mCheckRemoteCN=true; - np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE; + Vector<String> 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<String> compatnames = getOption("compat-names", 1, 2); + Vector<String> nonameremapping = getOption("no-name-remapping", 1, 1); + Vector<String> 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<String> 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<String> 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<String> verb = getOption("verb",1,1); - if(verb!=null){ - np.mVerb=verb.get(1); - } + Vector<String> 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<String> connectretry = getOption("connect-retry", 1, 1); - if(connectretry!=null) - np.mConnectRetry =connectretry.get(1); + Vector<String> connectretry = getOption("connect-retry", 1, 1); + if (connectretry != null) + np.mConnectRetry = connectretry.get(1); - Vector<String> connectretrymax = getOption("connect-retry-max", 1, 1); - if(connectretrymax!=null) - np.mConnectRetryMax =connectretrymax.get(1); + Vector<String> connectretrymax = getOption("connect-retry-max", 1, 1); + if (connectretrymax != null) + np.mConnectRetryMax = connectretrymax.get(1); Vector<Vector<String>> 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<String> 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<String> 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<Connection, Connection[]> conns = parseConnectionOptions(null); - np.mConnections =conns.second; + np.mConnections = conns.second; Vector<Vector<String>> connectionBlocks = getAllOption("connection", 1, 1); - if (np.mConnections.length > 0 && connectionBlocks !=null ) { - throw new ConfigParseError("Using a <connection> block and --remote is not allowed."); + if (np.mConnections.length > 0 && connectionBlocks != null) { + throw new ConfigParseError("Using a <connection> 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<String> 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<String> friendlyname = meta.get("FRIENDLY_NAME"); - if(friendlyname !=null && friendlyname.size() > 1) - np.mName=friendlyname.get(1); + Vector<String> friendlyname = meta.get("FRIENDLY_NAME"); + if (friendlyname != null && friendlyname.size() > 1) + np.mName = friendlyname.get(1); - Vector<String> ocusername = meta.get("USERNAME"); - if(ocusername !=null && ocusername.size() > 1) - np.mUsername=ocusername.get(1); + Vector<String> 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<Connection, Connection[]> 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<Connection, Connection[]> 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<String> port = getOption("port", 1,1); - if(port!=null){ + Vector<String> port = getOption("port", 1, 1); + if (port != null) { conn.mServerPort = port.get(1); } - Vector<String> rport = getOption("rport", 1,1); - if(rport!=null){ + Vector<String> rport = getOption("rport", 1, 1); + if (rport != null) { conn.mServerPort = rport.get(1); } - Vector<String> proto = getOption("proto", 1,1); - if(proto!=null){ - conn.mUseUdp=isUdpProto(proto.get(1)); + Vector<String> proto = getOption("proto", 1, 1); + if (proto != null) { + conn.mUseUdp = isUdpProto(proto.get(1)); } // Parse remote config - Vector<Vector<String>> remotes = getAllOption("remote",1,3); + Vector<Vector<String>> remotes = getAllOption("remote", 1, 3); // Assume that we need custom options if connectionDefault are set - if(connDefault!=null) { + if (connDefault != null) { for (Vector<Vector<String>> 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<Vector<String>>(); Connection[] connections = new Connection[remotes.size()]; - int i=0; - for (Vector<String> remote: remotes) { + int i = 0; + for (Vector<String> 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<Vector<String>> defgw) { - for (Vector<String> redirect: defgw) - for (int i=1;i<redirect.size();i++){ + for (Vector<String> 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<Vector<String>> option:options.values()) { + for (Vector<Vector<String>> option : options.values()) { - np.mCustomConfigOptions += getOptionStrings(option); + np.mCustomConfigOptions += getOptionStrings(option); - } - np.mUseCustomConfig=true; + } + np.mUseCustomConfig = true; - } - } + } + } boolean ignoreThisOption(Vector<String> option) { @@ -843,35 +817,35 @@ public class ConfigParser { private void fixup(VpnProfile np) { - if(np.mRemoteCN.equals(np.mServerName)) { - np.mRemoteCN=""; - } - } - - private Vector<String> getOption(String option, int minarg, int maxarg) throws ConfigParseError { - Vector<Vector<String>> alloptions = getAllOption(option, minarg, maxarg); - if(alloptions==null) - return null; - else - 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) - return null; - - for(Vector<String> 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<String> getOption(String option, int minarg, int maxarg) throws ConfigParseError { + Vector<Vector<String>> alloptions = getAllOption(option, minarg, maxarg); + if (alloptions == null) + return null; + else + 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) + return null; + + for (Vector<String> 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 578d95e7..3c1ec064 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -49,14 +49,14 @@ import de.blinkt.openvpn.core.VpnStatus.StateListener; 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_NONETWORK; -import static de.blinkt.openvpn.core.VpnStatus.ConnectionStatus.LEVEL_NOTCONNECTED; 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; public class OpenVPNService extends VpnService implements StateListener, Callback, ByteCountListener { - 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"; @@ -126,7 +126,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac synchronized (mProcessLock) { mProcessThread = null; } - mConnecttime = 0; VpnStatus.removeByteCountListener(this); unregisterDeviceStateReceiver(); ProfileManager.setConntectedVpnProfileDisconnected(this); @@ -555,9 +554,15 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac Collection<ipAddress> positiveIPv4Routes = mRoutes.getPositiveIPList(); Collection<ipAddress> 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()); } @@ -610,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()); @@ -810,8 +818,11 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac /* Workaround for Lollipop, it does not route traffic to the VPNs own network mask */ - if (mLocalIP.len <= 31 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) - addRoute(mLocalIP); + if (mLocalIP.len <= 31 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + CIDRIP interfaceRoute = new CIDRIP(mLocalIP.mIp, mLocalIP.len); + interfaceRoute.normalise(); + addRoute(interfaceRoute); + } // Configurations are sometimes really broken... @@ -868,7 +879,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac // Does not work :( String msg = getString(resid); String ticker = msg; - showNotification(msg + " " + logmessage, ticker, lowpriority , 0, level); + showNotification(msg + " " + logmessage, ticker, lowpriority, 0, level); + } } diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java index 298a6c40..d856feb7 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java @@ -32,7 +32,8 @@ import de.blinkt.openvpn.core.VpnStatus.LogItem; public class OpenVPNThread implements Runnable { private static final String DUMP_PATH_STRING = "Dump path: "; @SuppressLint("SdCardPath") - private static final String BROKEN_PIE_SUPPORT = "/data/data/de.blinkt.openvpn/cache/pievpn[1]: syntax error:"; + private static final String BROKEN_PIE_SUPPORT = "/data/data/de.blinkt.openvpn/cache/pievpn"; + private final static String BROKEN_PIE_SUPPORT2 = "syntax error"; private static final String TAG = "OpenVPN"; public static final int M_FATAL = (1 << 4); public static final int M_NONFATAL = (1 << 5); @@ -148,7 +149,7 @@ public class OpenVPNThread implements Runnable { if (logline.startsWith(DUMP_PATH_STRING)) mDumpPath = logline.substring(DUMP_PATH_STRING.length()); - if (logline.startsWith(BROKEN_PIE_SUPPORT)) + if (logline.startsWith(BROKEN_PIE_SUPPORT) || logline.contains(BROKEN_PIE_SUPPORT2)) mBrokenPie = true; diff --git a/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java b/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java index 199caa63..92bf9ad3 100644 --- a/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java +++ b/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java @@ -7,8 +7,6 @@ package de.blinkt.openvpn.fragments; import se.leap.bitmaskclient.R; -import se.leap.bitmaskclient.R; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java index afe1a638..6d368e11 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java +++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java @@ -310,10 +310,10 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn private Bundle bundleParameters(String username, String password) { Bundle parameters = new Bundle(); - if(!username.isEmpty() && !password.isEmpty()) { + if(!username.isEmpty()) parameters.putString(SessionDialog.USERNAME, username); + if(!password.isEmpty()) parameters.putString(SessionDialog.PASSWORD, password); - } return parameters; } @@ -344,12 +344,13 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn } public void sessionDialog(Bundle resultData) { + FragmentTransaction transaction = fragment_manager.removePreviousFragment(SessionDialog.TAG); - DialogFragment newFragment = SessionDialog.newInstance(); - if(resultData != null && !resultData.isEmpty() && fragment_manager.findFragmentByTag(SessionDialog.TAG) == null) { + DialogFragment newFragment = new SessionDialog(); + if(resultData != null && !resultData.isEmpty()) { newFragment.setArguments(resultData); - } + } newFragment.show(transaction, SessionDialog.TAG); } diff --git a/app/src/main/java/se/leap/bitmaskclient/SessionDialog.java b/app/src/main/java/se/leap/bitmaskclient/SessionDialog.java index fd9ca851..9025564b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/SessionDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/SessionDialog.java @@ -26,6 +26,8 @@ import android.view.View; import android.widget.EditText; import android.widget.TextView; +import org.jetbrains.annotations.NotNull; + import butterknife.ButterKnife; import butterknife.InjectView; @@ -56,43 +58,29 @@ public class SessionDialog extends DialogFragment{ @InjectView(R.id.password_entered) EditText password_field; - private static SessionDialog dialog; - private static boolean is_eip_pending = false; + + public SessionDialog() { + setArguments(Bundle.EMPTY); + } public AlertDialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); LayoutInflater inflater = getActivity().getLayoutInflater(); View view = inflater.inflate(R.layout.session_dialog, null); ButterKnife.inject(this, view); - - if(!username_field.getText().toString().isEmpty() && password_field.isFocusable()) { - password_field.requestFocus(); - } Bundle arguments = getArguments(); - if (arguments != null) { - is_eip_pending = arguments.getBoolean(EipFragment.IS_PENDING, false); - if (arguments.containsKey(PASSWORD_INVALID_LENGTH)) - password_field.setError(getString(R.string.error_not_valid_password_user_message)); - if (arguments.containsKey(USERNAME)) { - String username = arguments.getString(USERNAME); - username_field.setText(username); - } - if (arguments.containsKey(USERNAME_MISSING)) { - username_field.setError(getString(R.string.username_ask)); - } - if(arguments.containsKey(getString(R.string.user_message))) - user_message.setText(arguments.getString(getString(R.string.user_message))); - else - user_message.setVisibility(View.GONE); + if (arguments != Bundle.EMPTY) { + setUp(arguments); } builder.setView(view) .setPositiveButton(R.string.login_button, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - String username = username_field.getText().toString(); - String password = password_field.getText().toString(); + String username = getEnteredUsername(); + String password = getEnteredPassword(); dialog.dismiss(); interface_with_Dashboard.logIn(username, password); } @@ -105,8 +93,9 @@ public class SessionDialog extends DialogFragment{ }) .setNeutralButton(R.string.signup_button, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - String username = username_field.getText().toString(); - String password = password_field.getText().toString(); + String username = getEnteredUsername(); + String password = getEnteredPassword(); + dialog.dismiss(); interface_with_Dashboard.signUp(username, password); } }); @@ -114,6 +103,35 @@ public class SessionDialog extends DialogFragment{ return builder.create(); } + private void setUp(Bundle arguments) { + is_eip_pending = arguments.getBoolean(EipFragment.IS_PENDING, false); + if (arguments.containsKey(PASSWORD_INVALID_LENGTH)) + password_field.setError(getString(R.string.error_not_valid_password_user_message)); + if (arguments.containsKey(USERNAME)) { + String username = arguments.getString(USERNAME); + username_field.setText(username); + } + if (arguments.containsKey(USERNAME_MISSING)) { + username_field.setError(getString(R.string.username_ask)); + } + if(arguments.containsKey(getString(R.string.user_message))) + user_message.setText(arguments.getString(getString(R.string.user_message))); + else + user_message.setVisibility(View.GONE); + + if(!username_field.getText().toString().isEmpty() && password_field.isFocusable()) + password_field.requestFocus(); + + } + + private String getEnteredUsername() { + return username_field.getText().toString(); + } + + private String getEnteredPassword() { + return password_field.getText().toString(); + } + /** * Interface used to communicate SessionDialog with Dashboard. @@ -128,16 +146,6 @@ public class SessionDialog extends DialogFragment{ } SessionDialogInterface interface_with_Dashboard; - - /** - * @return a new instance of this DialogFragment. - */ - public static DialogFragment newInstance() { - if(dialog == null) - dialog = new SessionDialog(); - - return dialog; - } @Override public void onAttach(Activity activity) { 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 Binary files differnew file mode 100644 index 00000000..f42a0e2d --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_check_white_24dp.png 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 Binary files differnew file mode 100644 index 00000000..e91f9048 --- /dev/null +++ b/app/src/main/res/drawable-mdpi/ic_check_white_24dp.png 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 Binary files differnew file mode 100644 index 00000000..e5024472 --- /dev/null +++ b/app/src/main/res/drawable-xhdpi/ic_check_white_24dp.png 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 Binary files differnew file mode 100644 index 00000000..6e03d54c --- /dev/null +++ b/app/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png 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 Binary files differnew file mode 100644 index 00000000..87892840 --- /dev/null +++ b/app/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png 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 @@ <LinearLayout android:background="@drawable/white_rect" android:elevation="1dp" + android:orientation="vertical" android:layout_height="wrap_content" android:layout_width="match_parent"> 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 @@ <resources> <drawable name="ic_menu_close_clear_cancel">@drawable/ic_close_white_24dp</drawable> <drawable name="ic_menu_share">@drawable/ic_share_white_24dp </drawable> + <drawable name="ic_menu_save">@drawable/ic_check_white_24dp</drawable> <drawable name="ic_menu_view">@drawable/ic_filter_list_white_24dp</drawable> <drawable name="ic_menu_delete">@drawable/ic_delete_white_24dp</drawable> <drawable name="ic_menu_delete_grey">@drawable/ic_delete_grey600_24dp</drawable> - - <drawable name="ic_menu_edit">@drawable/ic_edit_white_24dp</drawable> </resources> 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 @@ <string name="show_log">Show log</string> <string name="faq_android_clients">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+</string> <string name="faq_androids_clients_title">Differences between the OpenVPN Android clients</string> + <string name="ignore_multicast_route">Ignoring multicast route: %s</string> </resources> |