From 5b95785060adace6b48a69d560051261233d954d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Sat, 6 Feb 2016 13:00:53 +0100 Subject: Update ics-openvpn --- .../main/java/de/blinkt/openvpn/core/CIDRIP.java | 2 +- .../java/de/blinkt/openvpn/core/ConfigParser.java | 54 ++++- .../java/de/blinkt/openvpn/core/Connection.java | 7 +- .../blinkt/openvpn/core/DeviceStateReceiver.java | 83 +++++-- .../blinkt/openvpn/core/ICSOpenVPNApplication.java | 4 +- .../openvpn/core/LollipopDeviceStateListener.java | 2 +- .../java/de/blinkt/openvpn/core/NativeUtils.java | 25 +- .../java/de/blinkt/openvpn/core/NetworkSpace.java | 32 +-- .../de/blinkt/openvpn/core/OpenVPNManagement.java | 10 +- .../de/blinkt/openvpn/core/OpenVPNService.java | 108 +++++++-- .../java/de/blinkt/openvpn/core/OpenVPNThread.java | 2 +- .../openvpn/core/OpenVpnManagementThread.java | 251 +++++++++++---------- .../java/de/blinkt/openvpn/core/PRNGFixes.java | 2 +- .../de/blinkt/openvpn/core/ProfileManager.java | 7 +- .../de/blinkt/openvpn/core/ProxyDetection.java | 2 +- .../de/blinkt/openvpn/core/VPNLaunchHelper.java | 29 ++- .../java/de/blinkt/openvpn/core/VpnStatus.java | 161 +++++++++---- .../java/de/blinkt/openvpn/core/X509Utils.java | 71 ++++-- 18 files changed, 585 insertions(+), 267 deletions(-) (limited to 'app/src/main/java/de/blinkt/openvpn/core') diff --git a/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java b/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java index 94ed8a0b..07f2152f 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java +++ b/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ 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 232c454b..80a15c54 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java +++ b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ @@ -37,6 +37,8 @@ public class ConfigParser { public void parseConfig(Reader reader) throws IOException, ConfigParseError { + HashMap optionAliases = new HashMap<>(); + optionAliases.put("server-poll-timeout", "timeout-connect"); BufferedReader br = new BufferedReader(reader); @@ -48,9 +50,15 @@ public class ConfigParser { if (line == null) break; - if (lineno == 1 && (line.startsWith("PK\003\004") - || (line.startsWith("PK\007\008")))) - throw new ConfigParseError("Input looks like a ZIP Archive. Import is only possible for OpenVPN config files (.ovpn/.conf)"); + if (lineno == 1) { + if ((line.startsWith("PK\003\004") + || (line.startsWith("PK\007\008")))) { + throw new ConfigParseError("Input looks like a ZIP Archive. Import is only possible for OpenVPN config files (.ovpn/.conf)"); + } + if (line.startsWith("\uFEFF")) { + line = line.substring(1); + } + } // Check for OpenVPN Access Server Meta information if (line.startsWith("# OVPN_ACCESS_SERVER_")) { @@ -70,6 +78,9 @@ public class ConfigParser { checkinlinefile(args, br); String optionname = args.get(0); + if (optionAliases.get(optionname)!=null) + optionname = optionAliases.get(optionname); + if (!options.containsKey(optionname)) { options.put(optionname, new Vector>()); } @@ -137,7 +148,7 @@ public class ConfigParser { } - public class ConfigParseError extends Exception { + public static class ConfigParseError extends Exception { private static final long serialVersionUID = -60L; public ConfigParseError(String msg) { @@ -388,6 +399,10 @@ public class ConfigParser { np.mCustomRoutesv6 = customIPv6Routes; } + Vector routeNoPull = getOption("route-nopull", 1, 1); + if (routeNoPull!=null) + np.mRoutenopull=true; + // Also recognize tls-auth [inline] direction ... Vector> tlsauthoptions = getAllOption("tls-auth", 1, 2); if (tlsauthoptions != null) { @@ -567,6 +582,9 @@ public class ConfigParser { if (getOption("persist-tun", 0, 0) != null) np.mPersistTun = true; + if (getOption("push-peer-info", 0, 0) != null) + np.mPushPeerInfo = true; + Vector connectretry = getOption("connect-retry", 1, 1); if (connectretry != null) np.mConnectRetry = connectretry.get(1); @@ -709,8 +727,18 @@ public class ConfigParser { conn.mUseUdp = isUdpProto(proto.get(1)); } + Vector connectTimeout = getOption("connect-timeout", 1, 1); + if (connectTimeout != null) { + try { + conn.mConnectTimeout = Integer.parseInt(connectTimeout.get(1)); + } catch (NumberFormatException nfe) { + throw new ConfigParseError(String.format("Argument to connect-timeout (%s) must to be an integer: %s", + connectTimeout.get(1), nfe.getLocalizedMessage())); + + } + } - // Parse remote config + // Parse remote config Vector> remotes = getAllOption("remote", 1, 3); @@ -838,13 +866,21 @@ public class ConfigParser { return false; } + //! Generate options for custom options private String getOptionStrings(Vector> option) { String custom = ""; for (Vector optionsline : option) { if (!ignoreThisOption(optionsline)) { - for (String arg : optionsline) - custom += VpnProfile.openVpnEscape(arg) + " "; - custom += "\n"; + // Check if option had been inlined and inline again + if (optionsline.size() == 2 && "extra-certs".equals(optionsline.get(0)) ) { + custom += VpnProfile.insertFileData(optionsline.get(0), optionsline.get(1)); + + + } else { + for (String arg : optionsline) + custom += VpnProfile.openVpnEscape(arg) + " "; + custom += "\n"; + } } } return custom; diff --git a/app/src/main/java/de/blinkt/openvpn/core/Connection.java b/app/src/main/java/de/blinkt/openvpn/core/Connection.java index b10664ce..3455450b 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/Connection.java +++ b/app/src/main/java/de/blinkt/openvpn/core/Connection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ @@ -16,6 +16,7 @@ public class Connection implements Serializable, Cloneable { public String mCustomConfiguration=""; public boolean mUseCustomConfig=false; public boolean mEnabled=true; + public int mConnectTimeout = 0; private static final long serialVersionUID = 92031902903829089L; @@ -33,6 +34,10 @@ public class Connection implements Serializable, Cloneable { else cfg += " tcp-client\n"; + if (mConnectTimeout!=0) + cfg += String.format(" connect-timeout %d\n" , mConnectTimeout); + + if (!TextUtils.isEmpty(mCustomConfiguration) && mUseCustomConfig) { cfg += mCustomConfiguration; cfg += "\n"; diff --git a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java index 4ccf5472..40684af3 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java +++ b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ @@ -12,15 +12,19 @@ import android.content.SharedPreferences; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.NetworkInfo.State; +import android.os.Handler; import android.preference.PreferenceManager; + import se.leap.bitmaskclient.R; import de.blinkt.openvpn.core.VpnStatus.ByteCountListener; import java.util.LinkedList; +import java.util.Objects; import static de.blinkt.openvpn.core.OpenVPNManagement.pauseReason; -public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountListener { +public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountListener, OpenVPNManagement.PausedStateCallback { + private final Handler mDisconnectHandler; private int lastNetwork = -1; private OpenVPNManagement mManagement; @@ -29,12 +33,36 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL // Data traffic limit in bytes private final long TRAFFIC_LIMIT = 64 * 1024; + // Time to wait after network disconnect to pause the VPN + private final int DISCONNECT_WAIT = 20; + connectState network = connectState.DISCONNECTED; connectState screen = connectState.SHOULDBECONNECTED; connectState userpause = connectState.SHOULDBECONNECTED; private String lastStateMsg = null; + private java.lang.Runnable mDelayDisconnectRunnable = new Runnable() { + @Override + public void run() { + if (!(network == connectState.PENDINGDISCONNECT)) + return; + + network = connectState.DISCONNECTED; + + // Set screen state to be disconnected if disconnect pending + if (screen == connectState.PENDINGDISCONNECT) + screen = connectState.DISCONNECTED; + + mManagement.pause(getPauseReason()); + } + }; + private NetworkInfo lastConnectedNetwork; + + @Override + public boolean shouldBeRunning() { + return shouldBeConnected(); + } enum connectState { SHOULDBECONNECTED, @@ -54,6 +82,7 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL LinkedList trafficdata = new LinkedList(); + @Override public void updateByteCount(long in, long out, long diffIn, long diffOut) { if (screen != connectState.PENDINGDISCONNECT) @@ -99,6 +128,8 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL public DeviceStateReceiver(OpenVPNManagement magnagement) { super(); mManagement = magnagement; + mManagement.setPauseCallback(this); + mDisconnectHandler = new Handler(); } @@ -113,7 +144,7 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL boolean screenOffPause = prefs.getBoolean("screenoff", false); if (screenOffPause) { - if (ProfileManager.getLastConnectedVpn()!=null && !ProfileManager.getLastConnectedVpn().mPersistTun) + if (ProfileManager.getLastConnectedVpn() != null && !ProfileManager.getLastConnectedVpn().mPersistTun) VpnStatus.logError(R.string.screen_nopersistenttun); screen = connectState.PENDINGDISCONNECT; @@ -126,6 +157,8 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL boolean connected = shouldBeConnected(); screen = connectState.SHOULDBECONNECTED; + /* We should connect now, cancel any outstanding disconnect timer */ + mDisconnectHandler.removeCallbacks(mDelayDisconnectRunnable); /* should be connected has changed because the screen is on now, connect the VPN */ if (shouldBeConnected() != connected) mManagement.resume(); @@ -140,6 +173,10 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL private void fillTrafficData() { trafficdata.add(new Datapoint(System.currentTimeMillis(), TRAFFIC_LIMIT)); } + public static boolean equalsObj(Object a, Object b) { + return (a == null) ? (b == null) : a.equals(b); + } + public void networkStateChange(Context context) { @@ -175,34 +212,49 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL if (networkInfo != null && networkInfo.getState() == State.CONNECTED) { int newnet = networkInfo.getType(); + + boolean pendingDisconnect = (network == connectState.PENDINGDISCONNECT); network = connectState.SHOULDBECONNECTED; - if (lastNetwork != newnet) { + boolean sameNetwork; + if (lastConnectedNetwork == null + || lastConnectedNetwork.getType() != networkInfo.getType() + || !equalsObj(lastConnectedNetwork.getExtraInfo(), networkInfo.getExtraInfo()) + ) + sameNetwork = false; + else + sameNetwork = true; + + /* Same network, connection still 'established' */ + if (pendingDisconnect && sameNetwork) { + mDisconnectHandler.removeCallbacks(mDelayDisconnectRunnable); + // Reprotect the sockets just be sure + mManagement.networkChange(true); + } else { + /* Different network or connection not established anymore */ + if (screen == connectState.PENDINGDISCONNECT) screen = connectState.DISCONNECTED; if (shouldBeConnected()) { - if (lastNetwork == -1) { - mManagement.resume(); - } else { - mManagement.networkChange(); + mDisconnectHandler.removeCallbacks(mDelayDisconnectRunnable); - } + if (pendingDisconnect || !sameNetwork) + mManagement.networkChange(sameNetwork); + else + mManagement.resume(); } lastNetwork = newnet; + lastConnectedNetwork = networkInfo; } } else if (networkInfo == null) { // Not connected, stop openvpn, set last connected network to no network lastNetwork = -1; if (sendusr1) { - network = connectState.DISCONNECTED; - - // Set screen state to be disconnected if disconnect pending - if (screen == connectState.PENDINGDISCONNECT) - screen = connectState.DISCONNECTED; + network = connectState.PENDINGDISCONNECT; + mDisconnectHandler.postDelayed(mDelayDisconnectRunnable, DISCONNECT_WAIT * 1000); - mManagement.pause(getPauseReason()); } } @@ -213,6 +265,7 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL } + public boolean isUserPaused() { return userpause == connectState.DISCONNECTED; } diff --git a/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java b/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java index 6e9e63c5..db3ae751 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java +++ b/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ @@ -37,5 +37,7 @@ public class ICSOpenVPNApplication extends Application { if (BuildConfig.DEBUG) { //ACRA.init(this); } + + VpnStatus.initLogCache(getApplicationContext().getCacheDir()); } } diff --git a/app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java b/app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java index 440458e4..04e4e8b4 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java +++ b/app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ diff --git a/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java b/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java index f67b7730..ea003d41 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java +++ b/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java @@ -1,19 +1,26 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ package de.blinkt.openvpn.core; +import android.os.Build; + import java.security.InvalidKeyException; public class NativeUtils { - public static native byte[] rsasign(byte[] input,int pkey) throws InvalidKeyException; - public static native String[] getIfconfig() throws IllegalArgumentException; - static native void jniclose(int fdint); - - static { - System.loadLibrary("stlport_shared"); - System.loadLibrary("opvpnutil"); - } + public static native byte[] rsasign(byte[] input, int pkey) throws InvalidKeyException; + + public static native String[] getIfconfig() throws IllegalArgumentException; + + static native void jniclose(int fdint); + + public static native String getNativeAPI(); + + static { + System.loadLibrary("opvpnutil"); + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) + System.loadLibrary("jbcrypto"); + } } 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 c86f9e44..eb6d4d42 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java +++ b/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java @@ -1,28 +1,28 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ package de.blinkt.openvpn.core; import android.os.Build; +import android.support.annotation.NonNull; import android.text.TextUtils; import junit.framework.Assert; -import org.jetbrains.annotations.NotNull; - import java.math.BigInteger; import java.net.Inet6Address; -import java.util.*; +import java.util.Collection; +import java.util.Locale; +import java.util.PriorityQueue; +import java.util.TreeSet; +import java.util.Vector; import se.leap.bitmaskclient.BuildConfig; public class NetworkSpace { - - - static class ipAddress implements Comparable { private BigInteger netAddress; public int networkMask; @@ -38,7 +38,7 @@ public class NetworkSpace { * 2. smaller networks are returned as smaller */ @Override - public int compareTo(@NotNull ipAddress another) { + public int compareTo(@NonNull ipAddress another) { int comp = getFirstAddress().compareTo(another.getFirstAddress()); if (comp != 0) return comp; @@ -159,16 +159,20 @@ public class NetworkSpace { String getIPv6Address() { if (BuildConfig.DEBUG) Assert.assertTrue (!isV4); BigInteger r = netAddress; - if (r.compareTo(BigInteger.ZERO)==0 && networkMask==0) - return "::"; Vector parts = new Vector(); - while (r.compareTo(BigInteger.ZERO) == 1) { - parts.add(0, String.format(Locale.US, "%x", r.mod(BigInteger.valueOf(0x10000)).longValue())); + while (r.compareTo(BigInteger.ZERO) == 1 || parts.size() <3) { + long part = r.mod(BigInteger.valueOf(0x10000)).longValue(); + if (part!=0) + parts.add(0, String.format(Locale.US, "%x", part)); + else + parts.add(0, ""); r = r.shiftRight(16); } - - return TextUtils.join(":", parts); + String ipv6str = TextUtils.join(":", parts); + while (ipv6str.contains(":::")) + ipv6str = ipv6str.replace(":::", "::"); + return ipv6str; } public boolean containsNet(ipAddress network) { diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java index 1f28c77d..2771fa6a 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java @@ -1,11 +1,15 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ package de.blinkt.openvpn.core; public interface OpenVPNManagement { + interface PausedStateCallback { + boolean shouldBeRunning(); + } + enum pauseReason { noNetwork, userPause, @@ -25,5 +29,7 @@ public interface OpenVPNManagement { /* * Rebind the interface */ - void networkChange(); + void networkChange(boolean sameNetwork); + + void setPauseCallback(PausedStateCallback callback); } 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 f9cb9a86..17be29b0 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ @@ -10,15 +10,18 @@ import android.annotation.TargetApi; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.UiModeManager; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.net.ConnectivityManager; import android.net.VpnService; import android.os.Binder; import android.os.Build; +import android.os.Handler; import android.os.Handler.Callback; import android.os.IBinder; import android.os.Message; @@ -27,6 +30,7 @@ import android.preference.PreferenceManager; import android.system.OsConstants; import android.text.TextUtils; import android.util.Log; +import android.widget.Toast; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -81,6 +85,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac private String mLastTunCfg; private String mRemoteGW; private final Object mProcessLock = new Object(); + private Handler guiHandler; + private Toast mlastToast; // From: http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java public static String humanReadableByteCount(long bytes, boolean mbit) { @@ -109,6 +115,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac @Override public void onRevoke() { + VpnStatus.logInfo(R.string.permission_revoked); mManagement.stopVPN(); endVpnService(); } @@ -135,7 +142,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac } } - private void showNotification(String msg, String tickerText, boolean lowpriority, long when, ConnectionStatus status) { + private void showNotification(final String msg, String tickerText, boolean lowpriority, long when, ConnectionStatus status) { String ns = Context.NOTIFICATION_SERVICE; NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns); @@ -164,6 +171,9 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) jbNotificationExtras(lowpriority, nbuilder); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + lpNotificationExtras(nbuilder); + if (tickerText != null && !tickerText.equals("")) nbuilder.setTicker(tickerText); @@ -173,6 +183,33 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac mNotificationManager.notify(OPENVPN_STATUS, notification); //startForeground(OPENVPN_STATUS, notification); + + // Check if running on a TV + if(runningOnAndroidTV() && !lowpriority) + guiHandler.post(new Runnable() { + + @Override + public void run() { + + if (mlastToast!=null) + mlastToast.cancel(); + String toastText = String.format(Locale.getDefault(), "%s - %s", mProfile.mName, msg); + mlastToast = Toast.makeText(getBaseContext(), toastText, Toast.LENGTH_SHORT); + mlastToast.show(); + } + }); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private void lpNotificationExtras(Notification.Builder nbuilder) { + nbuilder.setCategory(Notification.CATEGORY_SERVICE); + nbuilder.setLocalOnly(true); + + } + + private boolean runningOnAndroidTV() { + UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE); + return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION; } private int getIconByConnectionStatus(ConnectionStatus level) { @@ -215,20 +252,20 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac disconnectVPN.setAction(DISCONNECT_VPN); PendingIntent disconnectPendingIntent = PendingIntent.getActivity(this, 0, disconnectVPN, 0); - nbuilder.addAction(android.R.drawable.ic_menu_close_clear_cancel, + nbuilder.addAction(R.drawable.ic_menu_close_clear_cancel, getString(R.string.cancel_connection), disconnectPendingIntent); Intent pauseVPN = new Intent(this, OpenVPNService.class); if (mDeviceStateReceiver == null || !mDeviceStateReceiver.isUserPaused()) { pauseVPN.setAction(PAUSE_VPN); PendingIntent pauseVPNPending = PendingIntent.getService(this, 0, pauseVPN, 0); - nbuilder.addAction(android.R.drawable.ic_media_pause, + nbuilder.addAction(R.drawable.ic_menu_pause, getString(R.string.pauseVPN), pauseVPNPending); } else { pauseVPN.setAction(RESUME_VPN); PendingIntent resumeVPNPending = PendingIntent.getService(this, 0, pauseVPN, 0); - nbuilder.addAction(android.R.drawable.ic_media_play, + nbuilder.addAction(R.drawable.ic_menu_play, getString(R.string.resumevpn), resumeVPNPending); } @@ -297,6 +334,9 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac VpnStatus.addStateListener(this); VpnStatus.addByteCountListener(this); + guiHandler = new Handler(getMainLooper()); + + if (intent != null && PAUSE_VPN.equals(intent.getAction())) { if (mDeviceStateReceiver != null) mDeviceStateReceiver.userPause(true); @@ -454,6 +494,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac } // Just in case unregister for state VpnStatus.removeStateListener(this); + VpnStatus.flushLog(); } @@ -536,6 +577,26 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac Collection positiveIPv4Routes = mRoutes.getPositiveIPList(); Collection positiveIPv6Routes = mRoutesv6.getPositiveIPList(); + if ("samsung".equals(Build.BRAND) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mDnslist.size() >= 1) { + // Check if the first DNS Server is in the VPN range + try { + ipAddress dnsServer = new ipAddress(new CIDRIP(mDnslist.get(0), 32), true); + boolean dnsIncluded=false; + for (ipAddress net : positiveIPv4Routes) { + if (net.containsNet(dnsServer)) { + dnsIncluded = true; + } + } + if (!dnsIncluded) { + String samsungwarning = String.format("Warning Samsung Android 5.0+ devices ignore DNS servers outside the VPN range. To enable DNS resolution a route to your DNS Server (%s) has been added.", mDnslist.get(0)); + VpnStatus.logWarning(samsungwarning); + positiveIPv4Routes.add(dnsServer); + } + } catch (Exception e) { + VpnStatus.logError("Error parsing DNS Server IP: " + mDnslist.get(0)); + } + } + ipAddress multicastRange = new ipAddress(new CIDRIP("224.0.0.0", 3), true); for (NetworkSpace.ipAddress route : positiveIPv4Routes) { @@ -558,24 +619,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac } } - if ("samsung".equals(Build.BRAND) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mDnslist.size() >= 1) { - // Check if the first DNS Server is in the VPN range - try { - ipAddress dnsServer = new ipAddress(new CIDRIP(mDnslist.get(0), 32), true); - boolean dnsIncluded=false; - for (ipAddress net : positiveIPv4Routes) { - if (net.containsNet(dnsServer)) { - dnsIncluded = true; - } - } - if (!dnsIncluded) { - String samsungwarning = String.format("Warning Samsung Android 5.0+ devices ignore DNS servers outside the VPN range. To enable DNS add a custom route to your DNS Server (%s) or change to a DNS inside your VPN range", mDnslist.get(0)); - VpnStatus.logWarning(samsungwarning); - } - } catch (Exception e) { - VpnStatus.logError("Error parsing DNS Server IP: " + mDnslist.get(0)); - } - } + if (mDomain != null) @@ -672,12 +716,14 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void setAllowedVpnPackages(Builder builder) { + boolean atLeastOneAllowedApp=false; for (String pkg : mProfile.mAllowedAppsVpn) { try { if (mProfile.mAllowedAppsVpnAreDisallowed) { builder.addDisallowedApplication(pkg); } else { builder.addAllowedApplication(pkg); + atLeastOneAllowedApp = true; } } catch (PackageManager.NameNotFoundException e) { mProfile.mAllowedAppsVpn.remove(pkg); @@ -685,6 +731,15 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac } } + if (!mProfile.mAllowedAppsVpnAreDisallowed && !atLeastOneAllowedApp) { + VpnStatus.logDebug(R.string.no_allowed_app, getPackageName()); + try { + builder.addAllowedApplication(getPackageName()); + } catch (PackageManager.NameNotFoundException e) { + VpnStatus.logError("This should not happen: " + e.getLocalizedMessage()); + } + } + if (mProfile.mAllowedAppsVpnAreDisallowed) { VpnStatus.logDebug(R.string.disallowed_vpn_apps_info, TextUtils.join(", ", mProfile.mAllowedAppsVpn)); } else { @@ -839,7 +894,9 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac } else if (level == LEVEL_CONNECTED) { mDisplayBytecount = true; mConnecttime = System.currentTimeMillis(); - lowpriority = true; + if (!runningOnAndroidTV()) + lowpriority = true; + String ns = Context.NOTIFICATION_SERVICE; NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns); mNotificationManager.cancel(OPENVPN_STATUS); @@ -852,7 +909,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac // CONNECTED // Does not work :( String msg = getString(resid); - // showNotification(msg + " " + logmessage, msg, lowpriority, 0, level); + // showNotification(VpnStatus.getLastCleanLogMessage(this), + // msg, 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 d856feb7..10bc9e87 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java index 1c3b3362..4c550f47 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java @@ -1,23 +1,20 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ package de.blinkt.openvpn.core; import android.content.Context; -import android.content.SharedPreferences; import android.net.LocalServerSocket; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.os.ParcelFileDescriptor; -import android.preference.PreferenceManager; +import android.support.annotation.NonNull; import android.util.Log; import junit.framework.Assert; -import org.jetbrains.annotations.NotNull; - import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; @@ -42,30 +39,23 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { private LocalSocket mSocket; private VpnProfile mProfile; private OpenVPNService mOpenVPNService; - private LinkedList mFDList = new LinkedList(); + private LinkedList mFDList = new LinkedList<>(); private LocalServerSocket mServerSocket; - private boolean mReleaseHold = true; private boolean mWaitingForRelease = false; private long mLastHoldRelease = 0; - private static final Vector active = new Vector(); + private static final Vector active = new Vector<>(); private LocalSocket mServerSocketLocal; private pauseReason lastPauseReason = pauseReason.noNetwork; + private PausedStateCallback mPauseCallback; public OpenVpnManagementThread(VpnProfile profile, OpenVPNService openVpnService) { mProfile = profile; mOpenVPNService = openVpnService; - - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(openVpnService); - boolean managemeNetworkState = prefs.getBoolean("netchangereconnect", true); - if (managemeNetworkState) - mReleaseHold = false; - } - public boolean openManagementInterface(@NotNull Context c) { + public boolean openManagementInterface(@NonNull Context c) { // Could take a while to open connection int tries = 8; @@ -74,7 +64,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { mServerSocketLocal = new LocalSocket(); - while (tries > 0 && !mServerSocketLocal.isConnected()) { + while (tries > 0 && !mServerSocketLocal.isBound()) { try { mServerSocketLocal.bind(new LocalSocketAddress(socketName, LocalSocketAddress.Namespace.FILESYSTEM)); @@ -82,7 +72,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { // wait 300 ms before retrying try { Thread.sleep(300); - } catch (InterruptedException e1) { + } catch (InterruptedException ignored) { } } @@ -167,7 +157,6 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { //! Hack O Rama 2000! private void protectFileDescriptor(FileDescriptor fd) { - Exception exp; try { Method getInt = FileDescriptor.class.getDeclaredMethod("getInt$"); int fdint = (Integer) getInt.invoke(fd); @@ -183,20 +172,12 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { //pfd.close(); NativeUtils.jniclose(fdint); return; - } catch (NoSuchMethodException e) { - exp = e; - } catch (IllegalArgumentException e) { - exp = e; - } catch (IllegalAccessException e) { - exp = e; - } catch (InvocationTargetException e) { - exp = e; - } catch (NullPointerException e) { - exp = e; + } catch (NoSuchMethodException | IllegalArgumentException | InvocationTargetException | IllegalAccessException | NullPointerException e) { + VpnStatus.logException("Failed to retrieve fd from socket (" + fd + ")", e); } Log.d("Openvpn", "Failed to retrieve fd from socket: " + fd); - VpnStatus.logException("Failed to retrieve fd from socket (" + fd + ")", exp); + } private String processInput(String pendingInput) { @@ -225,31 +206,41 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { String argument = parts[1]; - if (cmd.equals("INFO")) { + switch (cmd) { + case "INFO": /* Ignore greeting from management */ - return; - } else if (cmd.equals("PASSWORD")) { - processPWCommand(argument); - } else if (cmd.equals("HOLD")) { - handleHold(); - } else if (cmd.equals("NEED-OK")) { - processNeedCommand(argument); - } else if (cmd.equals("BYTECOUNT")) { - processByteCount(argument); - } else if (cmd.equals("STATE")) { - processState(argument); - } else if (cmd.equals("PROXY")) { - processProxyCMD(argument); - } else if (cmd.equals("LOG")) { - processLogMessage(argument); - } else if (cmd.equals("RSA_SIGN")) { - processSignCommand(argument); - } else { - VpnStatus.logWarning("MGMT: Got unrecognized command" + command); - Log.i(TAG, "Got unrecognized command" + command); + return; + case "PASSWORD": + processPWCommand(argument); + break; + case "HOLD": + handleHold(); + break; + case "NEED-OK": + processNeedCommand(argument); + break; + case "BYTECOUNT": + processByteCount(argument); + break; + case "STATE": + processState(argument); + break; + case "PROXY": + processProxyCMD(argument); + break; + case "LOG": + processLogMessage(argument); + break; + case "RSA_SIGN": + processSignCommand(argument); + break; + default: + VpnStatus.logWarning("MGMT: Got unrecognized command" + command); + Log.i(TAG, "Got unrecognized command" + command); + break; } } else if (command.startsWith("SUCCESS:")) { - /* Ignore this kind of message too */ + /* Ignore this kind of message too */ return; } else if (command.startsWith("PROTECTFD: ")) { FileDescriptor fdtoprotect = mFDList.pollFirst(); @@ -278,16 +269,22 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { Log.d("OpenVPN", argument); VpnStatus.LogLevel level; - if (args[1].equals("I")) { - level = VpnStatus.LogLevel.INFO; - } else if (args[1].equals("W")) { - level = VpnStatus.LogLevel.WARNING; - } else if (args[1].equals("D")) { - level = VpnStatus.LogLevel.VERBOSE; - } else if (args[1].equals("F")) { - level = VpnStatus.LogLevel.ERROR; - } else { - level = VpnStatus.LogLevel.INFO; + switch (args[1]) { + case "I": + level = VpnStatus.LogLevel.INFO; + break; + case "W": + level = VpnStatus.LogLevel.WARNING; + break; + case "D": + level = VpnStatus.LogLevel.VERBOSE; + break; + case "F": + level = VpnStatus.LogLevel.ERROR; + break; + default: + level = VpnStatus.LogLevel.INFO; + break; } int ovpnlevel = Integer.parseInt(args[2]) & 0x0F; @@ -299,8 +296,15 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { VpnStatus.logMessageOpenVPN(level, ovpnlevel, msg); } + boolean shouldBeRunning() { + if (mPauseCallback == null) + return false; + else + return mPauseCallback.shouldBeRunning(); + } + private void handleHold() { - if (mReleaseHold) { + if (shouldBeRunning()) { releaseHoldCmd(); } else { mWaitingForRelease = true; @@ -327,11 +331,10 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { //managmentCommand("log on all\n"); } + public void releaseHold() { - mReleaseHold = true; if (mWaitingForRelease) releaseHoldCmd(); - } private void processProxyCMD(String argument) { @@ -391,15 +394,19 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { String status = "ok"; - if (needed.equals("PROTECTFD")) { - FileDescriptor fdtoprotect = mFDList.pollFirst(); - protectFileDescriptor(fdtoprotect); - } else if (needed.equals("DNSSERVER")) { - mOpenVPNService.addDNS(extra); - } else if (needed.equals("DNSDOMAIN")) { - mOpenVPNService.setDomain(extra); - } else if (needed.equals("ROUTE")) { - String[] routeparts = extra.split(" "); + switch (needed) { + case "PROTECTFD": + FileDescriptor fdtoprotect = mFDList.pollFirst(); + protectFileDescriptor(fdtoprotect); + break; + case "DNSSERVER": + mOpenVPNService.addDNS(extra); + break; + case "DNSDOMAIN": + mOpenVPNService.setDomain(extra); + break; + case "ROUTE": { + String[] routeparts = extra.split(" "); /* buf_printf (&out, "%s %s %s dev %s", network, netmask, gateway, rgi->iface); @@ -407,38 +414,46 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { buf_printf (&out, "%s %s %s", network, netmask, gateway); */ - if (routeparts.length == 5) { - if (BuildConfig.DEBUG) Assert.assertEquals("dev", routeparts[3]); - mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], routeparts[4]); - } else if (routeparts.length >= 3) { - mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], null); - } else { - VpnStatus.logError("Unrecognized ROUTE cmd:" + Arrays.toString(routeparts) + " | " + argument); + if (routeparts.length == 5) { + if (BuildConfig.DEBUG) Assert.assertEquals("dev", routeparts[3]); + mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], routeparts[4]); + } else if (routeparts.length >= 3) { + mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], null); + } else { + VpnStatus.logError("Unrecognized ROUTE cmd:" + Arrays.toString(routeparts) + " | " + argument); + } + + break; } + case "ROUTE6": { + String[] routeparts = extra.split(" "); + mOpenVPNService.addRoutev6(routeparts[0], routeparts[1]); + break; + } + case "IFCONFIG": + String[] ifconfigparts = extra.split(" "); + int mtu = Integer.parseInt(ifconfigparts[2]); + mOpenVPNService.setLocalIP(ifconfigparts[0], ifconfigparts[1], mtu, ifconfigparts[3]); + break; + case "IFCONFIG6": + mOpenVPNService.setLocalIPv6(extra); + + break; + case "PERSIST_TUN_ACTION": + // check if tun cfg stayed the same + status = mOpenVPNService.getTunReopenStatus(); + break; + case "OPENTUN": + if (sendTunFD(needed, extra)) + return; + else + status = "cancel"; + // This not nice or anything but setFileDescriptors accepts only FilDescriptor class :( - } else if (needed.equals("ROUTE6")) { - String[] routeparts = extra.split(" "); - mOpenVPNService.addRoutev6(routeparts[0], routeparts[1]); - } else if (needed.equals("IFCONFIG")) { - String[] ifconfigparts = extra.split(" "); - int mtu = Integer.parseInt(ifconfigparts[2]); - mOpenVPNService.setLocalIP(ifconfigparts[0], ifconfigparts[1], mtu, ifconfigparts[3]); - } else if (needed.equals("IFCONFIG6")) { - mOpenVPNService.setLocalIPv6(extra); - - } else if (needed.equals("PERSIST_TUN_ACTION")) { - // check if tun cfg stayed the same - status = mOpenVPNService.getTunReopenStatus(); - } else if (needed.equals("OPENTUN")) { - if (sendTunFD(needed, extra)) + break; + default: + Log.e(TAG, "Unkown needok command " + argument); return; - else - status = "cancel"; - // This not nice or anything but setFileDescriptors accepts only FilDescriptor class :( - - } else { - Log.e(TAG, "Unkown needok command " + argument); - return; } String cmd = String.format("needok '%s' %s\n", needed, status); @@ -446,7 +461,6 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { } private boolean sendTunFD(String needed, String extra) { - Exception exp; if (!extra.equals("tun")) { // We only support tun VpnStatus.logError(String.format("Device type %s requested, but only tun is possible with the Android API, sorry!", extra)); @@ -480,18 +494,10 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { pfd.close(); return true; - } catch (NoSuchMethodException e) { - exp = e; - } catch (IllegalArgumentException e) { - exp = e; - } catch (IllegalAccessException e) { - exp = e; - } catch (InvocationTargetException e) { - exp = e; - } catch (IOException e) { - exp = e; + } catch (NoSuchMethodException | IllegalArgumentException | InvocationTargetException | + IOException | IllegalAccessException exp) { + VpnStatus.logException("Could not send fd over socket", exp); } - VpnStatus.logException("Could not send fd over socket", exp); return false; } @@ -559,14 +565,21 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { } @Override - public void networkChange() { - if (!mWaitingForRelease) + public void networkChange(boolean samenetwork) { + if (mWaitingForRelease) + releaseHold(); + else if (samenetwork) + managmentCommand("network-change samenetwork\n"); + else managmentCommand("network-change\n"); } - public void signalusr1() { - mReleaseHold = false; + @Override + public void setPauseCallback(PausedStateCallback callback) { + mPauseCallback = callback; + } + public void signalusr1() { if (!mWaitingForRelease) managmentCommand("signal SIGUSR1\n"); else diff --git a/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java b/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java index a788426a..49a7eaa9 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java +++ b/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ diff --git a/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java b/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java index 086cdb44..4f9c219b 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java +++ b/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ @@ -128,6 +128,11 @@ public class ProfileManager { ProfileManager.tmpprofile = tmp; } + public static boolean isTempProfile() + { + return mLastConnectedVpn == tmpprofile; + } + public void saveProfile(Context context, VpnProfile profile) { ObjectOutputStream vpnfile; diff --git a/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java b/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java index 6e2abb13..34fb69f8 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java +++ b/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ diff --git a/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java b/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java index 47cb633c..f2cf8cec 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java +++ b/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ @@ -14,14 +14,15 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import java.util.Vector; import se.leap.bitmaskclient.R; import de.blinkt.openvpn.VpnProfile; public class VPNLaunchHelper { - private static final String MININONPIEVPN = "nopievpn"; - private static final String MINIPIEVPN = "pievpn"; + private static final String MININONPIEVPN = "nopie_openvpn"; + private static final String MINIPIEVPN = "pie_openvpn"; private static final String OVPNCONFIGFILE = "android.conf"; @@ -29,15 +30,22 @@ public class VPNLaunchHelper { static private String writeMiniVPN(Context context) { String[] abis; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) - abis = getSupportedAbisLollipop(); + abis = getSupportedABIsLollipop(); else + //noinspection deprecation abis = new String[]{Build.CPU_ABI, Build.CPU_ABI2}; + String nativeAPI = NativeUtils.getNativeAPI(); + if (!nativeAPI.equals(abis[0])) { + VpnStatus.logWarning(R.string.abi_mismatch, Arrays.toString(abis), nativeAPI); + abis = new String[] {nativeAPI}; + } + for (String abi: abis) { - File mvpnout = new File(context.getCacheDir(), getMiniVPNExecutableName() + "." + abi); - if ((mvpnout.exists() && mvpnout.canExecute()) || writeMiniVPNBinary(context, abi, mvpnout)) { - return mvpnout.getPath(); + File vpnExecutable = new File(context.getCacheDir(), getMiniVPNExecutableName() + "." + abi); + if ((vpnExecutable.exists() && vpnExecutable.canExecute()) || writeMiniVPNBinary(context, abi, vpnExecutable)) { + return vpnExecutable.getPath(); } } @@ -45,7 +53,7 @@ public class VPNLaunchHelper { } @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private static String[] getSupportedAbisLollipop() { + private static String[] getSupportedABIsLollipop() { return Build.SUPPORTED_ABIS; } @@ -118,12 +126,13 @@ public class VPNLaunchHelper { public static void startOpenVpn(VpnProfile startprofile, Context context) { - if(writeMiniVPN(context)==null) { + VpnStatus.logInfo(R.string.building_configration); + VpnStatus.updateStateString("VPN_GENERATE_CONFIG", "", R.string.building_configration, VpnStatus.ConnectionStatus.LEVEL_START); + if(writeMiniVPN(context)==null) { VpnStatus.logError("Error writing minivpn binary"); return; } - VpnStatus.logInfo(R.string.building_configration); Intent startVPN = startprofile.prepareStartService(context); if(startVPN!=null) diff --git a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java index 62a60643..3ac1595c 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java +++ b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java @@ -1,18 +1,19 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ package de.blinkt.openvpn.core; import android.annotation.SuppressLint; -import android.app.Activity; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.os.Build; +import android.os.HandlerThread; +import android.os.Message; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -74,6 +75,58 @@ public class VpnStatus { private static final int MAXLOGENTRIES = 1000; + + public static String getLastCleanLogMessage(Context c) { + String message = mLaststatemsg; + switch (mLastLevel) { + case LEVEL_CONNECTED: + String[] parts = mLaststatemsg.split(","); + /* + (a) the integer unix date/time, + (b) the state name, + 0 (c) optional descriptive string (used mostly on RECONNECTING + and EXITING to show the reason for the disconnect), + + 1 (d) optional TUN/TAP local IPv4 address + 2 (e) optional address of remote server, + 3 (f) optional port of remote server, + 4 (g) optional local address, + 5 (h) optional local port, and + 6 (i) optional TUN/TAP local IPv6 address. +*/ + // Return only the assigned IP addresses in the UI + if (parts.length >= 7) + message = String.format(Locale.US, "%s %s", parts[1], parts[6]); + break; + } + + while (message.endsWith(",")) + message = message.substring(0, message.length() - 1); + + String status = mLaststate; + if (status.equals("NOPROCESS")) + return message; + + String prefix = c.getString(mLastStateresid); + if (mLastStateresid == R.string.unknown_state) + message = status + message; + if (message.length() > 0) + prefix += ": "; + + return prefix + message; + + } + + public static void initLogCache(File cacheDir) { + Message m = mLogFileHandler.obtainMessage(LogFileHandler.LOG_INIT, cacheDir); + mLogFileHandler.sendMessage(m); + + } + + public static void flushLog() { + mLogFileHandler.sendEmptyMessage(LogFileHandler.FLUSH_TO_DISK); + } + public enum ConnectionStatus { LEVEL_CONNECTED, LEVEL_VPNPAUSED, @@ -81,6 +134,7 @@ public class VpnStatus { LEVEL_CONNECTING_NO_SERVER_REPLY_YET, LEVEL_NONETWORK, LEVEL_NOTCONNECTED, + LEVEL_START, LEVEL_AUTH_FAILED, LEVEL_WAITING_FOR_USER_INPUT, UNKNOWN_LEVEL @@ -128,18 +182,24 @@ public class VpnStatus { private static ConnectionStatus mLastLevel = ConnectionStatus.LEVEL_NOTCONNECTED; + private static final LogFileHandler mLogFileHandler; + static { logbuffer = new LinkedList<>(); logListener = new Vector<>(); stateListener = new Vector<>(); byteCountListener = new Vector<>(); + + HandlerThread mHandlerThread = new HandlerThread("LogFileWriter", Thread.MIN_PRIORITY); + mHandlerThread.start(); + mLogFileHandler = new LogFileHandler(mHandlerThread.getLooper()); + logInformation(); + } public static class LogItem implements Parcelable { - - private Object[] mArgs = null; private String mMessage = null; private int mRessourceId; @@ -261,6 +321,7 @@ public class VpnStatus { String version = "error getting version"; try { + @SuppressLint("PackageManagerGetSignatures") Signature raw = c.getPackageManager().getPackageInfo(c.getPackageName(), PackageManager.GET_SIGNATURES).signatures[0]; CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(raw.toByteArray())); @@ -287,11 +348,11 @@ public class VpnStatus { NoSuchAlgorithmException ignored) { } - Object[] argsext = Arrays.copyOf(mArgs, mArgs.length + 2); + Object[] argsext = Arrays.copyOf(mArgs, mArgs.length); argsext[argsext.length - 1] = apksign; argsext[argsext.length - 2] = version; - return c.getString(R.string.mobile_info_extended, argsext); + return c.getString(R.string.mobile_info, argsext); } @@ -310,12 +371,6 @@ public class VpnStatus { } } - public void saveLogToDisk(Context c) { - - File logOut = new File(c.getCacheDir(), "log.xml"); - - } - public interface LogListener { void newLog(LogItem logItem); } @@ -336,10 +391,12 @@ public class VpnStatus { public synchronized static void clearLog() { logbuffer.clear(); logInformation(); + mLogFileHandler.sendEmptyMessage(LogFileHandler.TRIM_LOG_FILE); } private static void logInformation() { - logInfo(R.string.mobile_info, Build.MODEL, Build.BOARD, Build.BRAND, Build.VERSION.SDK_INT); + logInfo(R.string.mobile_info, Build.MODEL, Build.BOARD, Build.BRAND, Build.VERSION.SDK_INT, + NativeUtils.getNativeAPI(), Build.VERSION.RELEASE, Build.ID, Build.FINGERPRINT, "", ""); } public synchronized static void addLogListener(LogListener ll) { @@ -369,32 +426,34 @@ public class VpnStatus { } private static int getLocalizedState(String state) { - if (state.equals("CONNECTING")) - return R.string.state_connecting; - else if (state.equals("WAIT")) - return R.string.state_wait; - else if (state.equals("AUTH")) - return R.string.state_auth; - else if (state.equals("GET_CONFIG")) - return R.string.state_get_config; - else if (state.equals("ASSIGN_IP")) - return R.string.state_assign_ip; - else if (state.equals("ADD_ROUTES")) - return R.string.state_add_routes; - else if (state.equals("CONNECTED")) - return R.string.state_connected; - else if (state.equals("DISCONNECTED")) - return R.string.state_disconnected; - else if (state.equals("RECONNECTING")) - return R.string.state_reconnecting; - else if (state.equals("EXITING")) - return R.string.state_exiting; - else if (state.equals("RESOLVE")) - return R.string.state_resolve; - else if (state.equals("TCP_CONNECT")) - return R.string.state_tcp_connect; - else - return R.string.unknown_state; + switch (state) { + case "CONNECTING": + return R.string.state_connecting; + case "WAIT": + return R.string.state_wait; + case "AUTH": + return R.string.state_auth; + case "GET_CONFIG": + return R.string.state_get_config; + case "ASSIGN_IP": + return R.string.state_assign_ip; + case "ADD_ROUTES": + return R.string.state_add_routes; + case "CONNECTED": + return R.string.state_connected; + case "DISCONNECTED": + return R.string.state_disconnected; + case "RECONNECTING": + return R.string.state_reconnecting; + case "EXITING": + return R.string.state_exiting; + case "RESOLVE": + return R.string.state_resolve; + case "TCP_CONNECT": + return R.string.state_tcp_connect; + default: + return R.string.unknown_state; + } } @@ -496,17 +555,33 @@ public class VpnStatus { newLogItem(new LogItem(LogLevel.DEBUG, resourceId, args)); } + private static void newLogItem(LogItem logItem) { + newLogItem(logItem, false); + } + + + synchronized static void newLogItem(LogItem logItem, boolean cachedLine) { + if (cachedLine) { + logbuffer.addFirst(logItem); + } else { + logbuffer.addLast(logItem); + Message m = mLogFileHandler.obtainMessage(LogFileHandler.LOG_MESSAGE, logItem); + mLogFileHandler.sendMessage(m); + } + + if (logbuffer.size() > MAXLOGENTRIES + MAXLOGENTRIES / 2) { + while (logbuffer.size() > MAXLOGENTRIES) + logbuffer.removeFirst(); + mLogFileHandler.sendMessage(mLogFileHandler.obtainMessage(LogFileHandler.TRIM_LOG_FILE)); + } - private synchronized static void newLogItem(LogItem logItem) { - logbuffer.addLast(logItem); - if (logbuffer.size() > MAXLOGENTRIES) - logbuffer.removeFirst(); for (LogListener ll : logListener) { ll.newLog(logItem); } } + public static void logError(String msg) { newLogItem(new LogItem(LogLevel.ERROR, msg)); diff --git a/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java b/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java index 0786967b..4048f0e0 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java +++ b/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java @@ -1,11 +1,12 @@ /* - * Copyright (c) 2012-2014 Arne Schwabe + * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ package de.blinkt.openvpn.core; import android.content.Context; +import android.content.res.Resources; import android.text.TextUtils; import se.leap.bitmaskclient.R; @@ -20,30 +21,39 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateFactory; +import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Date; import java.util.Hashtable; +import java.util.Vector; public class X509Utils { - public static Certificate getCertificateFromFile(String certfilename) throws FileNotFoundException, CertificateException { + public static Certificate[] getCertificatesFromFile(String certfilename) throws FileNotFoundException, CertificateException { CertificateFactory certFact = CertificateFactory.getInstance("X.509"); - InputStream inStream; - + Vector certificates = new Vector<>(); if(VpnProfile.isEmbedded(certfilename)) { - // The java certifcate reader is ... kind of stupid - // It does NOT ignore chars before the --BEGIN ... int subIndex = certfilename.indexOf("-----BEGIN CERTIFICATE-----"); - subIndex = Math.max(0,subIndex); - inStream = new ByteArrayInputStream(certfilename.substring(subIndex).getBytes()); + do { + // The java certifcate reader is ... kind of stupid + // It does NOT ignore chars before the --BEGIN ... + subIndex = Math.max(0, subIndex); + InputStream inStream = new ByteArrayInputStream(certfilename.substring(subIndex).getBytes()); + certificates.add(certFact.generateCertificate(inStream)); + subIndex = certfilename.indexOf("-----BEGIN CERTIFICATE-----", subIndex+1); + } while (subIndex > 0); + return certificates.toArray(new Certificate[certificates.size()]); } else { - inStream = new FileInputStream(certfilename); + InputStream inStream = new FileInputStream(certfilename); + return new Certificate[] {certFact.generateCertificate(inStream)}; } - return certFact.generateCertificate(inStream); } public static PemObject readPemObjectFromFile (String keyfilename) throws IOException { @@ -67,9 +77,10 @@ public class X509Utils { public static String getCertificateFriendlyName (Context c, String filename) { if(!TextUtils.isEmpty(filename)) { try { - X509Certificate cert = (X509Certificate) getCertificateFromFile(filename); - - return getCertificateFriendlyName(cert); + X509Certificate cert = (X509Certificate) getCertificatesFromFile(filename)[0]; + String friendlycn = getCertificateFriendlyName(cert); + friendlycn = getCertificateValidityString(cert, c.getResources()) + friendlycn; + return friendlycn; } catch (Exception e) { VpnStatus.logError("Could not read certificate" + e.getLocalizedMessage()); @@ -78,6 +89,40 @@ public class X509Utils { return c.getString(R.string.cannotparsecert); } + public static String getCertificateValidityString(X509Certificate cert, Resources res) { + try { + cert.checkValidity(); + } catch (CertificateExpiredException ce) { + return "EXPIRED: "; + } catch (CertificateNotYetValidException cny) { + return "NOT YET VALID: "; + } + + Date certNotAfter = cert.getNotAfter(); + Date now = new Date(); + long timeLeft = certNotAfter.getTime() - now.getTime(); // Time left in ms + + // More than 72h left, display days + // More than 3 months display months + if (timeLeft > 90l* 24 * 3600 * 1000) { + long months = getMonthsDifference(now, certNotAfter); + return res.getString(R.string.months_left, months); + } else if (timeLeft > 72 * 3600 * 1000) { + long days = timeLeft / (24 * 3600 * 1000); + return res.getString(R.string.days_left, days); + } else { + long hours = timeLeft / (3600 * 1000); + + return res.getString(R.string.hours_left, hours); + } + } + + public static int getMonthsDifference(Date date1, Date date2) { + int m1 = date1.getYear() * 12 + date1.getMonth(); + int m2 = date2.getYear() * 12 + date2.getMonth(); + return m2 - m1 + 1; + } + public static String getCertificateFriendlyName(X509Certificate cert) { X500Principal principal = cert.getSubjectX500Principal(); byte[] encodedSubject = principal.getEncoded(); -- cgit v1.2.3