From 7780f13af9ad5a58d3f87ff4cc76ded6e79e59f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Thu, 18 Sep 2014 19:48:20 +0200 Subject: Updated ics-openvpn, restart after 1' being killed Fixed notifications too (forgot to do it in another branch, it's #5964). --- .../src/main/java/de/blinkt/openvpn/LaunchVPN.java | 1 + .../main/java/de/blinkt/openvpn/VpnProfile.java | 16 +- .../blinkt/openvpn/activities/DisconnectVPN.java | 10 +- .../java/de/blinkt/openvpn/core/ConfigParser.java | 13 + .../blinkt/openvpn/core/DeviceStateReceiver.java | 2 +- .../blinkt/openvpn/core/ICSOpenVPNApplication.java | 31 +- .../java/de/blinkt/openvpn/core/NetworkSpace.java | 8 +- .../de/blinkt/openvpn/core/OpenVPNService.java | 814 +++++++++++++++++++++ .../java/de/blinkt/openvpn/core/OpenVPNThread.java | 4 +- .../openvpn/core/OpenVpnManagementThread.java | 786 ++++++++++---------- .../de/blinkt/openvpn/core/OpenVpnService.java | 781 -------------------- .../de/blinkt/openvpn/fragments/LogFragment.java | 8 +- .../main/src/main/res/values-ca/strings.xml | 2 +- .../main/src/main/res/values-de/strings.xml | 4 +- .../main/src/main/res/values-es/strings.xml | 7 +- .../main/src/main/res/values-fr/strings.xml | 3 +- .../main/src/main/res/values-ja/strings.xml | 2 +- .../main/src/main/res/values-pt/strings.xml | 139 ++++ .../main/src/main/res/values-ru/strings.xml | 7 +- .../main/src/main/res/values/strings.xml | 7 +- .../main/src/main/res/values/untranslatable.xml | 7 +- .../de/blinkt/openvpn/core/OpenVPNThreadv3.java | 8 +- 22 files changed, 1450 insertions(+), 1210 deletions(-) create mode 100644 ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java delete mode 100644 ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVpnService.java create mode 100755 ics-openvpn-stripped/main/src/main/res/values-pt/strings.xml (limited to 'ics-openvpn-stripped/main') diff --git a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/LaunchVPN.java b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/LaunchVPN.java index 218dd000..8c899efc 100644 --- a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/LaunchVPN.java +++ b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/LaunchVPN.java @@ -111,6 +111,7 @@ public class LaunchVPN extends Activity { } } + @Override protected void onActivityResult (int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); diff --git a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/VpnProfile.java b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/VpnProfile.java index e2ebfd42..25823442 100644 --- a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/VpnProfile.java +++ b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/VpnProfile.java @@ -45,7 +45,7 @@ import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import de.blinkt.openvpn.core.NativeUtils; -import de.blinkt.openvpn.core.OpenVpnService; +import de.blinkt.openvpn.core.OpenVPNService; import de.blinkt.openvpn.core.VpnStatus; import de.blinkt.openvpn.core.X509Utils; @@ -67,6 +67,7 @@ public class VpnProfile implements Serializable { private static final String OVPNCONFIGFILE = "android.conf"; public static final int MAXLOGLEVEL = 4; public static final int CURRENT_PROFILE_VERSION = 2; + public static final int DEFAULT_MSSFIX_SIZE = 1450; public static String DEFAULT_DNS1 = "8.8.8.8"; public static String DEFAULT_DNS2 = "8.8.4.4"; @@ -145,6 +146,9 @@ public class VpnProfile implements Serializable { private int mProfileVersion; public String mExcludedRoutes; public String mExcludedRoutesv6; + public int mMssFix =0; // -1 is default, + + public VpnProfile(String name) { mUuid = UUID.randomUUID(); @@ -184,6 +188,7 @@ public class VpnProfile implements Serializable { mCheckRemoteCN = false; mPersistTun = false; mAllowLocalLAN = true; + mMssFix = 0; } public UUID getUUID() { @@ -387,6 +392,13 @@ public class VpnProfile implements Serializable { } + if (mMssFix !=0){ + if (mMssFix!=1450) + cfg+=String.format("mssfix %d\n", mMssFix, Locale.US); + else + cfg+="mssfix\n"; + } + if (mNobind) cfg += "nobind\n"; @@ -568,7 +580,7 @@ public class VpnProfile implements Serializable { public Intent prepareIntent(Context context) { String prefix = context.getPackageName(); - Intent intent = new Intent(context, OpenVpnService.class); + Intent intent = new Intent(context, OpenVPNService.class); if (mAuthenticationType == VpnProfile.TYPE_KEYSTORE || mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) { if (getKeyStoreCertificates(context) == null) diff --git a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java index 8e418053..5910173a 100644 --- a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java +++ b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java @@ -6,14 +6,14 @@ import android.content.*; import android.os.IBinder; import se.leap.bitmaskclient.R; -import de.blinkt.openvpn.core.OpenVpnService; +import de.blinkt.openvpn.core.OpenVPNService; import de.blinkt.openvpn.core.ProfileManager; /** * Created by arne on 13.10.13. */ public class DisconnectVPN extends Activity implements DialogInterface.OnClickListener{ - protected OpenVpnService mService; + protected OpenVPNService mService; private ServiceConnection mConnection = new ServiceConnection() { @@ -22,7 +22,7 @@ public class DisconnectVPN extends Activity implements DialogInterface.OnClickLi public void onServiceConnected(ComponentName className, IBinder service) { // We've bound to LocalService, cast the IBinder and get LocalService instance - OpenVpnService.LocalBinder binder = (OpenVpnService.LocalBinder) service; + OpenVPNService.LocalBinder binder = (OpenVPNService.LocalBinder) service; mService = binder.getService(); } @@ -36,8 +36,8 @@ public class DisconnectVPN extends Activity implements DialogInterface.OnClickLi @Override protected void onResume() { super.onResume(); - Intent intent = new Intent(this, OpenVpnService.class); - intent.setAction(OpenVpnService.START_SERVICE); + Intent intent = new Intent(this, OpenVPNService.class); + intent.setAction(OpenVPNService.START_SERVICE); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); showDisconnectDialog(); } diff --git a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/ConfigParser.java b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/ConfigParser.java index d23b521f..9c3621e0 100644 --- a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/ConfigParser.java +++ b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/ConfigParser.java @@ -432,6 +432,19 @@ public class ConfigParser { throw new ConfigParseError("Sorry. Only tun mode is supported. See the FAQ for more detail"); } + Vector mssfix = getOption("mssfix",0,1); + + if (mssfix!=null) { + if (mssfix.size()>=2) { + try { + np.mMssFix=Integer.parseInt(mssfix.get(1)); + } catch(NumberFormatException e) { + throw new ConfigParseError("Argument to --mssfix has to be an integer"); + } + } else { + np.mMssFix = VpnProfile.DEFAULT_MSSFIX_SIZE; + } + } Vector mode =getOption("mode",1,1); diff --git a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java index 18c5f1d9..0126d08e 100644 --- a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java +++ b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java @@ -68,7 +68,7 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL if (windowtraffic < TRAFFIC_LIMIT) { screen = connectState.DISCONNECTED; VpnStatus.logInfo(R.string.screenoff_pause, - OpenVpnService.humanReadableByteCount(TRAFFIC_LIMIT, false), TRAFFIC_WINDOW); + OpenVPNService.humanReadableByteCount(TRAFFIC_LIMIT, false), TRAFFIC_WINDOW); mManagement.pause(getPauseReason()); } diff --git a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java index 1daa3433..485e5369 100644 --- a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java +++ b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java @@ -1,14 +1,37 @@ package de.blinkt.openvpn.core; - import android.app.Application; -/** - * Created by arne on 28.12.13. - */ +/* +import org.acra.ACRA; +import org.acra.ReportingInteractionMode; +import org.acra.annotation.ReportsCrashes; +*/ + +import se.leap.bitmaskclient.BuildConfig; +import se.leap.bitmaskclient.R; +import de.blinkt.openvpn.core.PRNGFixes; + +/* +@ReportsCrashes( + formKey = "", + formUri = "http://reports.blinkt.de/report-icsopenvpn", + reportType = org.acra.sender.HttpSender.Type.JSON, + httpMethod = org.acra.sender.HttpSender.Method.PUT, + formUriBasicAuthLogin="report-icsopenvpn", + formUriBasicAuthPassword="Tohd4neiF9Ai!!!!111eleven", + mode = ReportingInteractionMode.TOAST, + resToastText = R.string.crash_toast_text +) +*/ public class ICSOpenVPNApplication extends Application { @Override public void onCreate() { super.onCreate(); PRNGFixes.apply(); + + if (BuildConfig.DEBUG) { + //ACRA.init(this); + } } + } diff --git a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java index 81a17ef9..8c6cb1f5 100644 --- a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java +++ b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java @@ -76,8 +76,8 @@ public class NetworkSpace { netAddress = BigInteger.ZERO; for (byte b : address.getAddress()) { - s -= 16; - netAddress = netAddress.add(BigInteger.valueOf(b).shiftLeft(s)); + s -= 8; + netAddress = netAddress.add(BigInteger.valueOf((b & 0xFF)).shiftLeft(s)); } } @@ -152,12 +152,12 @@ public class NetworkSpace { String getIPv6Address() { if (BuildConfig.DEBUG) Assert.assertTrue (!isV4); BigInteger r = netAddress; - if (r.longValue() == 0) + 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(256)).longValue())); + parts.add(0, String.format(Locale.US, "%x", r.mod(BigInteger.valueOf(0x10000)).longValue())); r = r.shiftRight(16); } diff --git a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java new file mode 100644 index 00000000..743e7cc5 --- /dev/null +++ b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -0,0 +1,814 @@ +package de.blinkt.openvpn.core; + +import android.Manifest.permission; +import android.annotation.TargetApi; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.net.ConnectivityManager; +import android.net.VpnService; +import android.os.Binder; +import android.os.Build; +import android.os.Handler.Callback; +import android.os.IBinder; +import android.os.Message; +import android.os.ParcelFileDescriptor; +import android.preference.PreferenceManager; +import android.text.TextUtils; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Vector; + +import se.leap.bitmaskclient.BuildConfig; +import se.leap.bitmaskclient.R; +import de.blinkt.openvpn.VpnProfile; +import de.blinkt.openvpn.activities.DisconnectVPN; +import de.blinkt.openvpn.core.VpnStatus.ByteCountListener; +import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus; +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 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"; + public static final String DISCONNECT_VPN = "de.blinkt.openvpn.DISCONNECT_VPN"; + private static final String PAUSE_VPN = "de.blinkt.openvpn.PAUSE_VPN"; + private static final String RESUME_VPN = "se.leap.bitmaskclient.RESUME_VPN"; + private static final int OPENVPN_STATUS = 1; + private static boolean mNotificationAlwaysVisible = false; + private final Vector mDnslist = new Vector(); + private final NetworkSpace mRoutes = new NetworkSpace(); + private final NetworkSpace mRoutesv6 = new NetworkSpace(); + private final IBinder mBinder = new LocalBinder(); + private Thread mProcessThread = null; + private VpnProfile mProfile; + private String mDomain = null; + private CIDRIP mLocalIP = null; + private int mMtu; + private String mLocalIPv6 = null; + private DeviceStateReceiver mDeviceStateReceiver; + private boolean mDisplayBytecount = false; + private boolean mStarting = false; + private long mConnecttime; + private boolean mOvpn3 = false; + private OpenVPNManagement mManagement; + private String mLastTunCfg; + private String mRemoteGW; + private final Object mProcessLock = new Object(); + + // 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) { + if (mbit) + bytes = bytes * 8; + int unit = mbit ? 1000 : 1024; + if (bytes < unit) + return bytes + (mbit ? " bit" : " B"); + + int exp = (int) (Math.log(bytes) / Math.log(unit)); + String pre = (mbit ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (mbit ? "" : ""); + if (mbit) + return String.format(Locale.getDefault(), "%.1f %sbit", bytes / Math.pow(unit, exp), pre); + else + return String.format(Locale.getDefault(), "%.1f %sB", bytes / Math.pow(unit, exp), pre); + } + + @Override + public IBinder onBind(Intent intent) { + String action = intent.getAction(); + if (action != null && action.equals(START_SERVICE)) + return mBinder; + else + return super.onBind(intent); + } + + @Override + public void onRevoke() { + mManagement.stopVPN(); + endVpnService(); + } + + // Similar to revoke but do not try to stop process + public void processDied() { + endVpnService(); + } + + private void endVpnService() { + synchronized (mProcessLock) { + mProcessThread = null; + } + mConnecttime = 0; + VpnStatus.removeByteCountListener(this); + unregisterDeviceStateReceiver(); + ProfileManager.setConntectedVpnProfileDisconnected(this); + if (!mStarting) { + stopForeground(!mNotificationAlwaysVisible); + + if (!mNotificationAlwaysVisible) { + stopSelf(); + VpnStatus.removeStateListener(this); + } + } + } + + private void showNotification(String msg, String tickerText, boolean lowpriority, long when, ConnectionStatus status) { + String ns = Context.NOTIFICATION_SERVICE; + NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns); + + + int icon = getIconByConnectionStatus(status); + + android.app.Notification.Builder nbuilder = new Notification.Builder(this); + + if (mProfile != null) + nbuilder.setContentTitle(getString(R.string.notifcation_title, mProfile.mName)); + else + nbuilder.setContentTitle(getString(R.string.notifcation_title_notconnect)); + + nbuilder.setContentText(msg); + nbuilder.setOnlyAlertOnce(true); + nbuilder.setOngoing(true); + nbuilder.setContentIntent(getLogPendingIntent()); + nbuilder.setSmallIcon(icon); + + + if (when != 0) + nbuilder.setWhen(when); + + + // Try to set the priority available since API 16 (Jellybean) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) + jbNotificationExtras(lowpriority, nbuilder); + + if (tickerText != null && !tickerText.equals("")) + nbuilder.setTicker(tickerText); + + @SuppressWarnings("deprecation") + Notification notification = nbuilder.getNotification(); + + + mNotificationManager.notify(OPENVPN_STATUS, notification); + //startForeground(OPENVPN_STATUS, notification); + } + + private int getIconByConnectionStatus(ConnectionStatus level) { + switch (level) { + case LEVEL_CONNECTED: + return R.drawable.ic_stat_vpn; + case LEVEL_AUTH_FAILED: + case LEVEL_NONETWORK: + case LEVEL_NOTCONNECTED: + return R.drawable.ic_stat_vpn_offline; + case LEVEL_CONNECTING_NO_SERVER_REPLY_YET: + case LEVEL_WAITING_FOR_USER_INPUT: + return R.drawable.ic_stat_vpn_outline; + case LEVEL_CONNECTING_SERVER_REPLIED: + return R.drawable.ic_stat_vpn_empty_halo; + case LEVEL_VPNPAUSED: + return android.R.drawable.ic_media_pause; + case UNKNOWN_LEVEL: + default: + return R.drawable.ic_stat_vpn; + + } + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + private void jbNotificationExtras(boolean lowpriority, + android.app.Notification.Builder nbuilder) { + try { + if (lowpriority) { + Method setpriority = nbuilder.getClass().getMethod("setPriority", int.class); + // PRIORITY_MIN == -2 + setpriority.invoke(nbuilder, -2); + + Method setUsesChronometer = nbuilder.getClass().getMethod("setUsesChronometer", boolean.class); + setUsesChronometer.invoke(nbuilder, true); + + } + + Intent disconnectVPN = new Intent(this, DisconnectVPN.class); + disconnectVPN.setAction(DISCONNECT_VPN); + PendingIntent disconnectPendingIntent = PendingIntent.getActivity(this, 0, disconnectVPN, 0); + + nbuilder.addAction(android.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, + 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, + getString(R.string.resumevpn), resumeVPNPending); + } + + + //ignore exception + } catch (NoSuchMethodException nsm) { + VpnStatus.logException(nsm); + } catch (IllegalArgumentException e) { + VpnStatus.logException(e); + } catch (IllegalAccessException e) { + VpnStatus.logException(e); + } catch (InvocationTargetException e) { + VpnStatus.logException(e); + } + + } + + PendingIntent getLogPendingIntent() { + // Let the configure Button show the Log + Intent intent = new Intent(getBaseContext(), Dashboard.class); + intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + PendingIntent startLW = PendingIntent.getActivity(this, 0, intent, 0); + intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + return startLW; + + } + + synchronized void registerDeviceStateReceiver(OpenVPNManagement magnagement) { + // Registers BroadcastReceiver to track network connection changes. + IntentFilter filter = new IntentFilter(); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(Intent.ACTION_SCREEN_ON); + mDeviceStateReceiver = new DeviceStateReceiver(magnagement); + registerReceiver(mDeviceStateReceiver, filter); + VpnStatus.addByteCountListener(mDeviceStateReceiver); + } + + synchronized void unregisterDeviceStateReceiver() { + if (mDeviceStateReceiver != null) + try { + VpnStatus.removeByteCountListener(mDeviceStateReceiver); + this.unregisterReceiver(mDeviceStateReceiver); + } catch (IllegalArgumentException iae) { + // I don't know why this happens: + // java.lang.IllegalArgumentException: Receiver not registered: de.blinkt.openvpn.NetworkSateReceiver@41a61a10 + // Ignore for now ... + iae.printStackTrace(); + } + mDeviceStateReceiver = null; + } + + public void userPause(boolean shouldBePaused) { + if (mDeviceStateReceiver != null) + mDeviceStateReceiver.userPause(shouldBePaused); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + + if (intent != null && intent.getBooleanExtra(ALWAYS_SHOW_NOTIFICATION, false)) + mNotificationAlwaysVisible = true; + + VpnStatus.addStateListener(this); + VpnStatus.addByteCountListener(this); + + if (intent != null && PAUSE_VPN.equals(intent.getAction())) { + if (mDeviceStateReceiver != null) + mDeviceStateReceiver.userPause(true); + return START_NOT_STICKY; + } + + if (intent != null && RESUME_VPN.equals(intent.getAction())) { + if (mDeviceStateReceiver != null) + mDeviceStateReceiver.userPause(false); + return START_NOT_STICKY; + } + + + if (intent != null && START_SERVICE.equals(intent.getAction())) + return START_NOT_STICKY; + if (intent != null && START_SERVICE_STICKY.equals(intent.getAction())) { + return START_REDELIVER_INTENT; + } + + String UUID = "UUID"; + if (intent == null) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + android.util.Log.d("bitmaskclient", "UUID is " + prefs.getString(UUID, "")); + mProfile = ProfileManager.get(this, prefs.getString(UUID, "")); + android.util.Log.d("bitmaskclient", "mProfile is null? " + (mProfile == null)); + if(mProfile != null) + intent = mProfile.prepareIntent(getBaseContext()); + else + return START_NOT_STICKY; + } + if(mProfile != null) + android.util.Log.d("bitmaskclient", "mProfile != null"); + // Extract information from the intent. + String prefix = getPackageName(); + String[] argv = intent.getStringArrayExtra(prefix + ".ARGV"); + String nativelibdir = intent.getStringExtra(prefix + ".nativelib"); + String profileUUID = intent.getStringExtra(prefix + ".profileUUID"); + + mProfile = ProfileManager.get(this, profileUUID); + + String startTitle = getString(R.string.start_vpn_title, mProfile.mName); + String startTicker = getString(R.string.start_vpn_ticker, mProfile.mName); + showNotification(startTitle, startTicker, + false, 0, LEVEL_CONNECTING_NO_SERVER_REPLY_YET); + + // Set a flag that we are starting a new VPN + mStarting = true; + // Stop the previous session by interrupting the thread. + if (mManagement != null && mManagement.stopVPN()) + // an old was asked to exit, wait 1s + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + //ignore + } + + synchronized (mProcessLock) { + if (mProcessThread != null) { + mProcessThread.interrupt(); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + //ignore + } + } + } + // An old running VPN should now be exited + mStarting = false; + + // Start a new session by creating a new thread. + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + + mOvpn3 = prefs.getBoolean("ovpn3", false); + if (!"ovpn3".equals(BuildConfig.FLAVOR)) + mOvpn3 = false; + + + prefs.edit().putString(UUID, profileUUID).commit(); + // Open the Management Interface + if (!mOvpn3) { + + // start a Thread that handles incoming messages of the managment socket + OpenVpnManagementThread ovpnManagementThread = new OpenVpnManagementThread(mProfile, this); + if (ovpnManagementThread.openManagementInterface(this)) { + + Thread mSocketManagerThread = new Thread(ovpnManagementThread, "OpenVPNManagementThread"); + mSocketManagerThread.start(); + mManagement = ovpnManagementThread; + VpnStatus.logInfo("started Socket Thread"); + } else { + return START_NOT_STICKY; + } + } + + + Runnable processThread; + if (mOvpn3) { + + OpenVPNManagement mOpenVPN3 = instantiateOpenVPN3Core(); + processThread = (Runnable) mOpenVPN3; + mManagement = mOpenVPN3; + + + } else { + HashMap env = new HashMap(); + processThread = new OpenVPNThread(this, argv, env, nativelibdir); + } + + synchronized (mProcessLock) { + mProcessThread = new Thread(processThread, "OpenVPNProcessThread"); + mProcessThread.start(); + } + if (mDeviceStateReceiver != null) + unregisterDeviceStateReceiver(); + + registerDeviceStateReceiver(mManagement); + + + ProfileManager.setConnectedVpnProfile(this, mProfile); + + if (mProfile.mPersistTun) + return START_STICKY; + else + return START_NOT_STICKY; + } + + private OpenVPNManagement instantiateOpenVPN3Core() { + try { + Class cl = Class.forName("de.blinkt.openvpn.core.OpenVPNThreadv3"); + return (OpenVPNManagement) cl.getConstructor(OpenVPNService.class,VpnProfile.class).newInstance(this,mProfile); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public void onDestroy() { + synchronized (mProcessLock) { + if (mProcessThread != null) { + mManagement.stopVPN(); + } + } + + if (mDeviceStateReceiver != null) { + this.unregisterReceiver(mDeviceStateReceiver); + } + // Just in case unregister for state + VpnStatus.removeStateListener(this); + + } + + private String getTunConfigString() { + // The format of the string is not important, only that + // two identical configurations produce the same result + String cfg = "TUNCFG UNQIUE STRING ips:"; + + if (mLocalIP != null) + cfg += mLocalIP.toString(); + if (mLocalIPv6 != null) + cfg += mLocalIPv6; + + cfg += "routes: " + TextUtils.join("|", mRoutes.getNetworks(true)) + TextUtils.join("|", mRoutesv6.getNetworks(true)); + cfg += "excl. routes:" + TextUtils.join("|", mRoutes.getNetworks(false)) + TextUtils.join("|", mRoutesv6.getNetworks(false)); + cfg += "dns: " + TextUtils.join("|", mDnslist); + cfg += "domain: " + mDomain; + cfg += "mtu: " + mMtu; + return cfg; + } + + public ParcelFileDescriptor openTun() { + + //Debug.startMethodTracing(getExternalFilesDir(null).toString() + "/opentun.trace", 40* 1024 * 1024); + + Builder builder = new Builder(); + + VpnStatus.logInfo(R.string.last_openvpn_tun_config); + + + if (mLocalIP == null && mLocalIPv6 == null) { + VpnStatus.logError(getString(R.string.opentun_no_ipaddr)); + return null; + } + + if (mLocalIP != null) { + try { + builder.addAddress(mLocalIP.mIp, mLocalIP.len); + } catch (IllegalArgumentException iae) { + VpnStatus.logError(R.string.dns_add_error, mLocalIP, iae.getLocalizedMessage()); + return null; + } + } + + if (mLocalIPv6 != null) { + String[] ipv6parts = mLocalIPv6.split("/"); + try { + builder.addAddress(ipv6parts[0], Integer.parseInt(ipv6parts[1])); + } catch (IllegalArgumentException iae) { + VpnStatus.logError(R.string.ip_add_error, mLocalIPv6, iae.getLocalizedMessage()); + return null; + } + + } + + + for (String dns : mDnslist) { + try { + builder.addDnsServer(dns); + } catch (IllegalArgumentException iae) { + VpnStatus.logError(R.string.dns_add_error, dns, iae.getLocalizedMessage()); + } + } + + String release = Build.VERSION.RELEASE; + if ((Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT && !release.startsWith("4.4.3") + && !release.startsWith("4.4.4") && !release.startsWith("4.4.5") && !release.startsWith("4.4.6")) + && mMtu < 1280) { + VpnStatus.logInfo(String.format("Forcing MTU to 1280 instead of %d to workaround Android Bug #70916", mMtu)); + builder.setMtu(1280); + } else { + builder.setMtu(mMtu); + } + + Collection positiveIPv4Routes = mRoutes.getPositiveIPList(); + Collection positiveIPv6Routes = mRoutesv6.getPositiveIPList(); + + for (NetworkSpace.ipAddress route : positiveIPv4Routes) { + try { + builder.addRoute(route.getIPv4Address(), route.networkMask); + } catch (IllegalArgumentException ia) { + VpnStatus.logError(getString(R.string.route_rejected) + route + " " + ia.getLocalizedMessage()); + } + } + + for (NetworkSpace.ipAddress route6 : positiveIPv6Routes) { + try { + builder.addRoute(route6.getIPv6Address(), route6.networkMask); + } catch (IllegalArgumentException ia) { + VpnStatus.logError(getString(R.string.route_rejected) + route6 + " " + ia.getLocalizedMessage()); + } + } + + if (mDomain != null) + builder.addSearchDomain(mDomain); + + VpnStatus.logInfo(R.string.local_ip_info, mLocalIP.mIp, mLocalIP.len, mLocalIPv6, mMtu); + VpnStatus.logInfo(R.string.dns_server_info, TextUtils.join(", ", mDnslist), mDomain); + VpnStatus.logInfo(R.string.routes_info_incl, TextUtils.join(", ", mRoutes.getNetworks(true)), TextUtils.join(", ", mRoutesv6.getNetworks(true))); + VpnStatus.logInfo(R.string.routes_info_excl, TextUtils.join(", ", mRoutes.getNetworks(false)),TextUtils.join(", ", mRoutesv6.getNetworks(false))); + VpnStatus.logDebug(R.string.routes_debug, TextUtils.join(", ", positiveIPv4Routes), TextUtils.join(", ", positiveIPv6Routes)); + + String session = mProfile.mName; + if (mLocalIP != null && mLocalIPv6 != null) + session = getString(R.string.session_ipv6string, session, mLocalIP, mLocalIPv6); + else if (mLocalIP != null) + session = getString(R.string.session_ipv4string, session, mLocalIP); + + builder.setSession(session); + + // No DNS Server, log a warning + if (mDnslist.size() == 0) + VpnStatus.logInfo(R.string.warn_no_dns); + + mLastTunCfg = getTunConfigString(); + + // Reset information + mDnslist.clear(); + mRoutes.clear(); + mRoutesv6.clear(); + mLocalIP = null; + mLocalIPv6 = null; + mDomain = null; + + builder.setConfigureIntent(getLogPendingIntent()); + + try { + //Debug.stopMethodTracing(); + return builder.establish(); + } catch (Exception e) { + VpnStatus.logError(R.string.tun_open_error); + VpnStatus.logError(getString(R.string.error) + e.getLocalizedMessage()); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR1) { + VpnStatus.logError(R.string.tun_error_helpful); + } + return null; + } + + } + + public void addDNS(String dns) { + mDnslist.add(dns); + } + + public void setDomain(String domain) { + if (mDomain == null) { + mDomain = domain; + } + } + + /** Route that is always included, used by the v3 core */ + public void addRoute (CIDRIP route) { + mRoutes.addIP(route, true); + } + + public void addRoute (String dest, String mask, String gateway, String device) { + CIDRIP route = new CIDRIP(dest, mask); + boolean include = isAndroidTunDevice(device); + + NetworkSpace.ipAddress gatewayIP = new NetworkSpace.ipAddress(new CIDRIP(gateway, 32),false); + + if (mLocalIP==null) { + VpnStatus.logError("Local IP address unset but adding route?! This is broken! Please contact author with log"); + return; + } + NetworkSpace.ipAddress localNet = new NetworkSpace.ipAddress(mLocalIP,true); + if (localNet.containsNet(gatewayIP)) + include=true; + + if (gateway!= null && + (gateway.equals("255.255.255.255") || gateway.equals(mRemoteGW))) + include=true; + + + if (route.len == 32 && !mask.equals("255.255.255.255")) { + VpnStatus.logWarning(R.string.route_not_cidr, dest, mask); + } + + if (route.normalise()) + VpnStatus.logWarning(R.string.route_not_netip, dest, route.len, route.mIp); + + mRoutes.addIP(route, include); + } + + public void addRoutev6(String network, String device) { + String[] v6parts = network.split("/"); + boolean included = isAndroidTunDevice(device); + + // Tun is opened after ROUTE6, no device name may be present + + try { + Inet6Address ip = (Inet6Address) InetAddress.getAllByName(v6parts[0])[0]; + int mask = Integer.parseInt(v6parts[1]); + mRoutesv6.addIPv6(ip, mask, included); + + } catch (UnknownHostException e) { + VpnStatus.logException(e); + } + + + } + + private boolean isAndroidTunDevice(String device) { + return device!=null && + (device.startsWith("tun") || "(null)".equals(device) || "vpnservice-tun".equals(device)); + } + + public void setMtu(int mtu) { + mMtu = mtu; + } + + public void setLocalIP(CIDRIP cdrip) { + mLocalIP = cdrip; + } + + public void setLocalIP(String local, String netmask, int mtu, String mode) { + mLocalIP = new CIDRIP(local, netmask); + mMtu = mtu; + mRemoteGW=null; + + long netMaskAsInt = CIDRIP.getInt(netmask); + + if (mLocalIP.len == 32 && !netmask.equals("255.255.255.255")) { + // get the netmask as IP + + int masklen; + if ("net30".equals(mode)) + masklen = 30; + else + masklen = 31; + + int mask = ~( 1 << (32 - (mLocalIP.len +1))); + // Netmask is Ip address +/-1, assume net30/p2p with small net + if ((netMaskAsInt & mask) == (mLocalIP.getInt() & mask )) { + mLocalIP.len = masklen; + } else { + mLocalIP.len = 32; + if (!"p2p".equals(mode)) + VpnStatus.logWarning(R.string.ip_not_cidr, local, netmask, mode); + } + } + if (("p2p".equals(mode)) && mLocalIP.len < 32 || "net30".equals("net30") && mLocalIP.len < 30) { + VpnStatus.logWarning(R.string.ip_looks_like_subnet, local, netmask, mode); + } + + + // Configurations are sometimes really broken... + mRemoteGW=netmask; + } + + public void setLocalIPv6(String ipv6addr) { + mLocalIPv6 = ipv6addr; + } + + @Override + public void updateState(String state, String logmessage, int resid, ConnectionStatus level) { + // If the process is not running, ignore any state, + // Notification should be invisible in this state + doSendBroadcast(state, level); + if (mProcessThread == null && !mNotificationAlwaysVisible) + return; + + boolean lowpriority = false; + // Display byte count only after being connected + + { + if (level == LEVEL_WAITING_FOR_USER_INPUT) { + // The user is presented a dialog of some kind, no need to inform the user + // with a notifcation + return; + } else if (level == LEVEL_CONNECTED) { + mDisplayBytecount = true; + mConnecttime = System.currentTimeMillis(); + lowpriority = true; + if(mProfile.mPersistTun) { + NotificationManager ns = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + ns.cancel(OPENVPN_STATUS); + return; + } + } else if (level == LEVEL_NONETWORK || level == LEVEL_NOTCONNECTED) { + NotificationManager ns = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + ns.cancel(OPENVPN_STATUS); + return; + } else if (level != LEVEL_NOTCONNECTED && mConnecttime > 0) { + mDisplayBytecount = false; + String msg = "Traffic is blocked until the VPN becomes active."; + String ticker = msg; + showNotification(msg, ticker, lowpriority , 0, level); + return; + } else { + mDisplayBytecount = false; + } + + // Other notifications are shown, + // This also mean we are no longer connected, ignore bytecount messages until next + // CONNECTED + // Does not work :( + String msg = getString(resid); + String ticker = msg; + showNotification(msg + " " + logmessage, ticker, lowpriority , 0, level); + } + } + + private void doSendBroadcast(String state, ConnectionStatus level) { + Intent vpnstatus = new Intent(); + vpnstatus.setAction("de.blinkt.openvpn.VPN_STATUS"); + vpnstatus.putExtra("status", level.toString()); + vpnstatus.putExtra("detailstatus", state); + sendBroadcast(vpnstatus, permission.ACCESS_NETWORK_STATE); + } + + @Override + public void updateByteCount(long in, long out, long diffIn, long diffOut) { + if (mDisplayBytecount) { + String netstat = String.format(getString(R.string.statusline_bytecount), + humanReadableByteCount(in, false), + humanReadableByteCount(diffIn / OpenVPNManagement.mBytecountInterval, true), + humanReadableByteCount(out, false), + humanReadableByteCount(diffOut / OpenVPNManagement.mBytecountInterval, true)); + + boolean lowpriority = !mNotificationAlwaysVisible; + //showNotification(netstat, null, lowpriority, mConnecttime, LEVEL_CONNECTED); + } + + } + + @Override + public boolean handleMessage(Message msg) { + Runnable r = msg.getCallback(); + if (r != null) { + r.run(); + return true; + } else { + return false; + } + } + + public OpenVPNManagement getManagement() { + return mManagement; + } + + public String getTunReopenStatus() { + String currentConfiguration = getTunConfigString(); + if (currentConfiguration.equals(mLastTunCfg)) { + return "NOACTION"; + } else { + String release = Build.VERSION.RELEASE; + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT && !release.startsWith("4.4.3") + && !release.startsWith("4.4.4") && !release.startsWith("4.4.5") && !release.startsWith("4.4.6")) + // There will be probably no 4.4.4 or 4.4.5 version, so don't waste effort to do parsing here + return "OPEN_AFTER_CLOSE"; + else + return "OPEN_BEFORE_CLOSE"; + } + } + + public class LocalBinder extends Binder { + public OpenVPNService getService() { + // Return this instance of LocalService so clients can call public methods + return OpenVPNService.this; + } + } +} diff --git a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java index 67c05e7d..5fa2ab9e 100644 --- a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java +++ b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java @@ -33,11 +33,11 @@ public class OpenVPNThread implements Runnable { private String[] mArgv; private Process mProcess; private String mNativeDir; - private OpenVpnService mService; + private OpenVPNService mService; private String mDumpPath; private Map mProcessEnv; - public OpenVPNThread(OpenVpnService service,String[] argv, Map processEnv, String nativelibdir) + public OpenVPNThread(OpenVPNService service,String[] argv, Map processEnv, String nativelibdir) { mArgv = argv; mNativeDir = nativelibdir; diff --git a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java index e6e5be25..e200f210 100644 --- a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java +++ b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java @@ -33,49 +33,50 @@ import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus; public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { - private static final String TAG = "openvpn"; - private LocalSocket mSocket; - private VpnProfile mProfile; - private OpenVpnService mOpenVPNService; - private LinkedList mFDList=new LinkedList(); + private static final String TAG = "openvpn"; + private LocalSocket mSocket; + private VpnProfile mProfile; + private OpenVPNService mOpenVPNService; + private LinkedList mFDList = new LinkedList(); private LocalServerSocket mServerSocket; - private boolean mReleaseHold=true; - private boolean mWaitingForRelease=false; - private long mLastHoldRelease=0; + private boolean mReleaseHold = true; + private boolean mWaitingForRelease = false; + private long mLastHoldRelease = 0; - private static Vector active=new Vector(); + private static final Vector active = new Vector(); private LocalSocket mServerSocketLocal; private pauseReason lastPauseReason = pauseReason.noNetwork; - public OpenVpnManagementThread(VpnProfile profile, OpenVpnService openVpnService) { - mProfile = profile; - mOpenVPNService = openVpnService; - + 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; - } + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(openVpnService); + boolean managemeNetworkState = prefs.getBoolean("netchangereconnect", true); + if (managemeNetworkState) + mReleaseHold = false; + + } public boolean openManagementInterface(@NotNull Context c) { // Could take a while to open connection - int tries=8; + int tries = 8; - String socketName = (c.getCacheDir().getAbsolutePath() + "/" + "mgmtsocket"); + String socketName = (c.getCacheDir().getAbsolutePath() + "/" + "mgmtsocket"); // The mServerSocketLocal is transferred to the LocalServerSocket, ignore warning mServerSocketLocal = new LocalSocket(); - while(tries > 0 && !mServerSocketLocal.isConnected()) { + while (tries > 0 && !mServerSocketLocal.isConnected()) { try { mServerSocketLocal.bind(new LocalSocketAddress(socketName, LocalSocketAddress.Namespace.FILESYSTEM)); } catch (IOException e) { // wait 300 ms before retrying - try { Thread.sleep(300); + try { + Thread.sleep(300); } catch (InterruptedException e1) { } @@ -95,165 +96,168 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { } - public void managmentCommand(String cmd) { + public void managmentCommand(String cmd) { try { - if(mSocket!=null && mSocket.getOutputStream() !=null) { - mSocket.getOutputStream().write(cmd.getBytes()); - mSocket.getOutputStream().flush(); - } - }catch (IOException e) { - // Ignore socket stack traces + if (mSocket != null && mSocket.getOutputStream() != null) { + mSocket.getOutputStream().write(cmd.getBytes()); + mSocket.getOutputStream().flush(); + } + } catch (IOException e) { + // Ignore socket stack traces } - } + } - @Override - public void run() { - byte [] buffer =new byte[2048]; - // mSocket.setSoTimeout(5); // Setting a timeout cannot be that bad + @Override + public void run() { + byte[] buffer = new byte[2048]; + // mSocket.setSoTimeout(5); // Setting a timeout cannot be that bad - String pendingInput=""; - active.add(this); + String pendingInput = ""; + synchronized (active) { + active.add(this); + } - try { - // Wait for a client to connect - mSocket= mServerSocket.accept(); - InputStream instream = mSocket.getInputStream(); + try { + // Wait for a client to connect + mSocket = mServerSocket.accept(); + InputStream instream = mSocket.getInputStream(); // Close the management socket after client connected mServerSocket.close(); // Closing one of the two sockets also closes the other //mServerSocketLocal.close(); - while(true) { - int numbytesread = instream.read(buffer); - if(numbytesread==-1) - return; - - FileDescriptor[] fds = null; - try { - fds = mSocket.getAncillaryFileDescriptors(); - } catch (IOException e) { - VpnStatus.logException("Error reading fds from socket", e); - } - if(fds!=null){ - Collections.addAll(mFDList, fds); - } + while (true) { + int numbytesread = instream.read(buffer); + if (numbytesread == -1) + return; - String input = new String(buffer,0,numbytesread,"UTF-8"); + FileDescriptor[] fds = null; + try { + fds = mSocket.getAncillaryFileDescriptors(); + } catch (IOException e) { + VpnStatus.logException("Error reading fds from socket", e); + } + if (fds != null) { + Collections.addAll(mFDList, fds); + } - pendingInput += input; + String input = new String(buffer, 0, numbytesread, "UTF-8"); - pendingInput=processInput(pendingInput); + pendingInput += input; + pendingInput = processInput(pendingInput); - } - } catch (IOException e) { + } + } catch (IOException e) { if (!e.getMessage().equals("socket closed")) VpnStatus.logException(e); - } - active.remove(this); - } + } + synchronized (active) { + active.remove(this); + } + } - //! Hack O Rama 2000! - private void protectFileDescriptor(FileDescriptor fd) { - Exception exp; - try { - Method getInt = FileDescriptor.class.getDeclaredMethod("getInt$"); - int fdint = (Integer) getInt.invoke(fd); + //! Hack O Rama 2000! + private void protectFileDescriptor(FileDescriptor fd) { + Exception exp; + try { + Method getInt = FileDescriptor.class.getDeclaredMethod("getInt$"); + int fdint = (Integer) getInt.invoke(fd); - // You can even get more evil by parsing toString() and extract the int from that :) + // You can even get more evil by parsing toString() and extract the int from that :) - boolean result = mOpenVPNService.protect(fdint); + boolean result = mOpenVPNService.protect(fdint); if (!result) VpnStatus.logWarning("Could not protect VPN socket"); - //ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(fdint); - //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; - } + //ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(fdint); + //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; + } Log.d("Openvpn", "Failed to retrieve fd from socket: " + fd); - VpnStatus.logException("Failed to retrieve fd from socket (" + fd + ")" , exp); - } + VpnStatus.logException("Failed to retrieve fd from socket (" + fd + ")", exp); + } - private String processInput(String pendingInput) { + private String processInput(String pendingInput) { - while(pendingInput.contains("\n")) { - String[] tokens = pendingInput.split("\\r?\\n", 2); - processCommand(tokens[0]); - if(tokens.length == 1) - // No second part, newline was at the end - pendingInput=""; - else - pendingInput=tokens[1]; - } - return pendingInput; - } + while (pendingInput.contains("\n")) { + String[] tokens = pendingInput.split("\\r?\\n", 2); + processCommand(tokens[0]); + if (tokens.length == 1) + // No second part, newline was at the end + pendingInput = ""; + else + pendingInput = tokens[1]; + } + return pendingInput; + } - private void processCommand(String command) { + private void processCommand(String command) { //Log.i(TAG, "Line from managment" + command); if (command.startsWith(">") && command.contains(":")) { - String[] parts = command.split(":",2); - String cmd = parts[0].substring(1); - String argument = parts[1]; + String[] parts = command.split(":", 2); + String cmd = parts[0].substring(1); + String argument = parts[1]; - if(cmd.equals("INFO")) { - /* Ignore greeting from management */ + if (cmd.equals("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); - } - } else if (command.startsWith("SUCCESS:")) { + } 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); + } + } else if (command.startsWith("SUCCESS:")) { /* Ignore this kind of message too */ return; } else if (command.startsWith("PROTECTFD: ")) { FileDescriptor fdtoprotect = mFDList.pollFirst(); - if (fdtoprotect!=null) + if (fdtoprotect != null) protectFileDescriptor(fdtoprotect); - } else { - Log.i(TAG, "Got unrecognized line from managment" + command); - VpnStatus.logWarning("MGMT: Got unrecognized line from management:" + command); - } - } + } else { + Log.i(TAG, "Got unrecognized line from managment" + command); + VpnStatus.logWarning("MGMT: Got unrecognized line from management:" + command); + } + } private void processLogMessage(String argument) { - String[] args = argument.split(",",4); + String[] args = argument.split(",", 4); // 0 unix time stamp // 1 log level N,I,E etc. /* @@ -287,109 +291,110 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { if (msg.startsWith("MANAGEMENT: CMD")) ovpnlevel = Math.max(4, ovpnlevel); - VpnStatus.logMessageOpenVPN(level,ovpnlevel, msg); + VpnStatus.logMessageOpenVPN(level, ovpnlevel, msg); } private void handleHold() { - if(mReleaseHold) { - releaseHoldCmd(); - } else { - mWaitingForRelease=true; + if (mReleaseHold) { + releaseHoldCmd(); + } else { + mWaitingForRelease = true; VpnStatus.updateStatePause(lastPauseReason); - } - } - private void releaseHoldCmd() { - if ((System.currentTimeMillis()- mLastHoldRelease) < 5000) { - try { - Thread.sleep(3000); - } catch (InterruptedException e) { + } + } + + private void releaseHoldCmd() { + if ((System.currentTimeMillis() - mLastHoldRelease) < 5000) { + try { + Thread.sleep(3000); + } catch (InterruptedException ignored) { } - - } - mWaitingForRelease=false; - mLastHoldRelease = System.currentTimeMillis(); - managmentCommand("hold release\n"); - managmentCommand("bytecount " + mBytecountInterval + "\n"); + + } + mWaitingForRelease = false; + mLastHoldRelease = System.currentTimeMillis(); + managmentCommand("hold release\n"); + managmentCommand("bytecount " + mBytecountInterval + "\n"); managmentCommand("state on\n"); //managmentCommand("log on all\n"); - } - - public void releaseHold() { - mReleaseHold=true; - if(mWaitingForRelease) - releaseHoldCmd(); - - } - - private void processProxyCMD(String argument) { - String[] args = argument.split(",",3); - SocketAddress proxyaddr = ProxyDetection.detectProxy(mProfile); - - - if(args.length >= 2) { - String proto = args[1]; - if(proto.equals("UDP")) { - proxyaddr=null; - } - } - - if(proxyaddr instanceof InetSocketAddress ){ - InetSocketAddress isa = (InetSocketAddress) proxyaddr; - - VpnStatus.logInfo(R.string.using_proxy, isa.getHostName(), isa.getPort()); - - String proxycmd = String.format(Locale.ENGLISH,"proxy HTTP %s %d\n", isa.getHostName(),isa.getPort()); - managmentCommand(proxycmd); - } else { - managmentCommand("proxy NONE\n"); - } - - } - private void processState(String argument) { - String[] args = argument.split(",",3); - String currentstate = args[1]; - - if(args[2].equals(",,")) - VpnStatus.updateStateString(currentstate, ""); - else - VpnStatus.updateStateString(currentstate, args[2]); - } - - - private void processByteCount(String argument) { - // >BYTECOUNT:{BYTES_IN},{BYTES_OUT} - int comma = argument.indexOf(','); - long in = Long.parseLong(argument.substring(0, comma)); - long out = Long.parseLong(argument.substring(comma+1)); - - VpnStatus.updateByteCount(in, out); - - } - - - - private void processNeedCommand(String argument) { - int p1 =argument.indexOf('\''); - int p2 = argument.indexOf('\'',p1+1); - - String needed = argument.substring(p1+1, p2); - String extra = argument.split(":",2)[1]; - - 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(" "); + } + + public void releaseHold() { + mReleaseHold = true; + if (mWaitingForRelease) + releaseHoldCmd(); + + } + + private void processProxyCMD(String argument) { + String[] args = argument.split(",", 3); + SocketAddress proxyaddr = ProxyDetection.detectProxy(mProfile); + + + if (args.length >= 2) { + String proto = args[1]; + if (proto.equals("UDP")) { + proxyaddr = null; + } + } + + if (proxyaddr instanceof InetSocketAddress) { + InetSocketAddress isa = (InetSocketAddress) proxyaddr; + + VpnStatus.logInfo(R.string.using_proxy, isa.getHostName(), isa.getPort()); + + String proxycmd = String.format(Locale.ENGLISH, "proxy HTTP %s %d\n", isa.getHostName(), isa.getPort()); + managmentCommand(proxycmd); + } else { + managmentCommand("proxy NONE\n"); + } + + } + + private void processState(String argument) { + String[] args = argument.split(",", 3); + String currentstate = args[1]; + + if (args[2].equals(",,")) + VpnStatus.updateStateString(currentstate, ""); + else + VpnStatus.updateStateString(currentstate, args[2]); + } + + + private void processByteCount(String argument) { + // >BYTECOUNT:{BYTES_IN},{BYTES_OUT} + int comma = argument.indexOf(','); + long in = Long.parseLong(argument.substring(0, comma)); + long out = Long.parseLong(argument.substring(comma + 1)); + + VpnStatus.updateByteCount(in, out); + + } + + + private void processNeedCommand(String argument) { + int p1 = argument.indexOf('\''); + int p2 = argument.indexOf('\'', p1 + 1); + + String needed = argument.substring(p1 + 1, p2); + String extra = argument.split(":", 2)[1]; + + 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(" "); /* buf_printf (&out, "%s %s %s dev %s", network, netmask, gateway, rgi->iface); @@ -397,209 +402,208 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { buf_printf (&out, "%s %s %s", network, netmask, gateway); */ - if(routeparts.length==5) { + 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) { + } else if (routeparts.length >= 3) { mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], null); } else { VpnStatus.logError("Unrecognized ROUTE cmd:" + Arrays.toString(routeparts) + " | " + argument); } - } else if (needed.equals("ROUTE6")) { + } 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")) { + 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)) - 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); - managmentCommand(cmd); - } - - 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)); - - return false; - } - ParcelFileDescriptor pfd = mOpenVPNService.openTun(); - if(pfd==null) - return false; - - Method setInt; - int fdint = pfd.getFd(); - try { - setInt = FileDescriptor.class.getDeclaredMethod("setInt$",int.class); - FileDescriptor fdtosend = new FileDescriptor(); - - setInt.invoke(fdtosend,fdint); - - FileDescriptor[] fds = {fdtosend}; - mSocket.setFileDescriptorsForSend(fds); - - // Trigger a send so we can close the fd on our side of the channel - // The API documentation fails to mention that it will not reset the file descriptor to - // be send and will happily send the file descriptor on every write ... - String cmd = String.format("needok '%s' %s\n", needed, "ok"); - managmentCommand(cmd); - - // Set the FileDescriptor to null to stop this mad behavior - mSocket.setFileDescriptorsForSend(null); - - 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; - } - VpnStatus.logException("Could not send fd over socket" , exp); + if (sendTunFD(needed, extra)) + 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); + managmentCommand(cmd); + } + + 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)); + + return false; + } + ParcelFileDescriptor pfd = mOpenVPNService.openTun(); + if (pfd == null) + return false; + + Method setInt; + int fdint = pfd.getFd(); + try { + setInt = FileDescriptor.class.getDeclaredMethod("setInt$", int.class); + FileDescriptor fdtosend = new FileDescriptor(); + + setInt.invoke(fdtosend, fdint); + + FileDescriptor[] fds = {fdtosend}; + mSocket.setFileDescriptorsForSend(fds); + + // Trigger a send so we can close the fd on our side of the channel + // The API documentation fails to mention that it will not reset the file descriptor to + // be send and will happily send the file descriptor on every write ... + String cmd = String.format("needok '%s' %s\n", needed, "ok"); + managmentCommand(cmd); + + // Set the FileDescriptor to null to stop this mad behavior + mSocket.setFileDescriptorsForSend(null); + + 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; + } + VpnStatus.logException("Could not send fd over socket", exp); return false; - } - - private void processPWCommand(String argument) { - //argument has the form Need 'Private Key' password - // or ">PASSWORD:Verification Failed: '%s' ['%s']" - String needed; - - - - try{ - - int p1 = argument.indexOf('\''); - int p2 = argument.indexOf('\'',p1+1); - needed = argument.substring(p1+1, p2); - if (argument.startsWith("Verification Failed")) { - proccessPWFailed(needed, argument.substring(p2+1)); - return; - } - } catch (StringIndexOutOfBoundsException sioob) { - VpnStatus.logError("Could not parse management Password command: " + argument); - return; - } - - String pw=null; - - if(needed.equals("Private Key")) { - pw = mProfile.getPasswordPrivateKey(); - } else if (needed.equals("Auth")) { - String usercmd = String.format("username '%s' %s\n", - needed, VpnProfile.openVpnEscape(mProfile.mUsername)); - managmentCommand(usercmd); - pw = mProfile.getPasswordAuth(); - } - if(pw!=null) { - String cmd = String.format("password '%s' %s\n", needed, VpnProfile.openVpnEscape(pw)); - managmentCommand(cmd); - } else { - VpnStatus.logError(String.format("Openvpn requires Authentication type '%s' but no password/key information available", needed)); - } - - } - - - - - private void proccessPWFailed(String needed, String args) { - VpnStatus.updateStateString("AUTH_FAILED", needed + args, R.string.state_auth_failed, ConnectionStatus.LEVEL_AUTH_FAILED); - } - - - private static boolean stopOpenVPN() { - boolean sendCMD=false; - for (OpenVpnManagementThread mt: active){ - mt.managmentCommand("signal SIGINT\n"); - sendCMD=true; - try { - if(mt.mSocket !=null) - mt.mSocket.close(); - } catch (IOException e) { - // Ignore close error on already closed socket - } - } - return sendCMD; - } + } + + private void processPWCommand(String argument) { + //argument has the form Need 'Private Key' password + // or ">PASSWORD:Verification Failed: '%s' ['%s']" + String needed; + + + try { + + int p1 = argument.indexOf('\''); + int p2 = argument.indexOf('\'', p1 + 1); + needed = argument.substring(p1 + 1, p2); + if (argument.startsWith("Verification Failed")) { + proccessPWFailed(needed, argument.substring(p2 + 1)); + return; + } + } catch (StringIndexOutOfBoundsException sioob) { + VpnStatus.logError("Could not parse management Password command: " + argument); + return; + } + + String pw = null; + + if (needed.equals("Private Key")) { + pw = mProfile.getPasswordPrivateKey(); + } else if (needed.equals("Auth")) { + String usercmd = String.format("username '%s' %s\n", + needed, VpnProfile.openVpnEscape(mProfile.mUsername)); + managmentCommand(usercmd); + pw = mProfile.getPasswordAuth(); + } + if (pw != null) { + String cmd = String.format("password '%s' %s\n", needed, VpnProfile.openVpnEscape(pw)); + managmentCommand(cmd); + } else { + VpnStatus.logError(String.format("Openvpn requires Authentication type '%s' but no password/key information available", needed)); + } + + } + + + private void proccessPWFailed(String needed, String args) { + VpnStatus.updateStateString("AUTH_FAILED", needed + args, R.string.state_auth_failed, ConnectionStatus.LEVEL_AUTH_FAILED); + } + + + private static boolean stopOpenVPN() { + synchronized (active) { + boolean sendCMD = false; + for (OpenVpnManagementThread mt : active) { + mt.managmentCommand("signal SIGINT\n"); + sendCMD = true; + try { + if (mt.mSocket != null) + mt.mSocket.close(); + } catch (IOException e) { + // Ignore close error on already closed socket + } + } + return sendCMD; + } + } @Override public void networkChange() { - if(!mWaitingForRelease) + if (!mWaitingForRelease) managmentCommand("network-change\n"); } - public void signalusr1() { - mReleaseHold=false; + public void signalusr1() { + mReleaseHold = false; - if(!mWaitingForRelease) - managmentCommand("signal SIGUSR1\n"); + if (!mWaitingForRelease) + managmentCommand("signal SIGUSR1\n"); else // If signalusr1 is called update the state string // if there is another for stopping VpnStatus.updateStatePause(lastPauseReason); - } + } - public void reconnect() { - signalusr1(); - releaseHold(); - } + public void reconnect() { + signalusr1(); + releaseHold(); + } - private void processSignCommand(String b64data) { + private void processSignCommand(String b64data) { - String signed_string = mProfile.getSignedData(b64data); - if(signed_string==null) { + String signed_string = mProfile.getSignedData(b64data); + if (signed_string == null) { managmentCommand("rsa-sig\n"); managmentCommand("\nEND\n"); stopOpenVPN(); return; } managmentCommand("rsa-sig\n"); - managmentCommand(signed_string); + managmentCommand(signed_string); managmentCommand("\nEND\n"); - } + } - @Override - public void pause (pauseReason reason) { + @Override + public void pause(pauseReason reason) { lastPauseReason = reason; - signalusr1(); - } + signalusr1(); + } - @Override - public void resume() { - releaseHold(); + @Override + public void resume() { + releaseHold(); /* Reset the reason why we are disconnected */ lastPauseReason = pauseReason.noNetwork; - } + } - @Override - public boolean stopVPN() { - return stopOpenVPN(); - } + @Override + public boolean stopVPN() { + return stopOpenVPN(); + } } diff --git a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVpnService.java b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVpnService.java deleted file mode 100644 index 0cf93de3..00000000 --- a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/OpenVpnService.java +++ /dev/null @@ -1,781 +0,0 @@ -package de.blinkt.openvpn.core; - -import android.Manifest.permission; -import android.annotation.TargetApi; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.net.ConnectivityManager; -import android.net.VpnService; -import android.os.Binder; -import android.os.Build; -import android.os.Handler.Callback; -import android.os.IBinder; -import android.os.Message; -import android.os.ParcelFileDescriptor; -import android.preference.PreferenceManager; -import android.text.TextUtils; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Locale; -import java.util.Vector; - -import se.leap.bitmaskclient.BuildConfig; -import se.leap.bitmaskclient.R; -import de.blinkt.openvpn.VpnProfile; -import de.blinkt.openvpn.activities.DisconnectVPN; -import de.blinkt.openvpn.core.VpnStatus.ByteCountListener; -import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus; -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_CONNECTING_SERVER_REPLIED; -import static de.blinkt.openvpn.core.VpnStatus.ConnectionStatus.LEVEL_NONETWORK; -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 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"; - public static final String DISCONNECT_VPN = "de.blinkt.openvpn.DISCONNECT_VPN"; - private static final String PAUSE_VPN = "de.blinkt.openvpn.PAUSE_VPN"; - private static final String RESUME_VPN = "se.leap.bitmaskclient.RESUME_VPN"; - private static final int OPENVPN_STATUS = 1; - private static boolean mNotificationAlwaysVisible = false; - private final Vector mDnslist = new Vector(); - private final NetworkSpace mRoutes = new NetworkSpace(); - private final NetworkSpace mRoutesv6 = new NetworkSpace(); - private final IBinder mBinder = new LocalBinder(); - private Thread mProcessThread = null; - private VpnProfile mProfile; - private String mDomain = null; - private CIDRIP mLocalIP = null; - private int mMtu; - private String mLocalIPv6 = null; - private DeviceStateReceiver mDeviceStateReceiver; - private boolean mDisplayBytecount = false; - private boolean mStarting = false; - private long mConnecttime; - private boolean mOvpn3 = false; - private OpenVPNManagement mManagement; - private String mLastTunCfg; - private String mRemoteGW; - private Object mProcessLock = new Object(); - - // 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) { - if (mbit) - bytes = bytes * 8; - int unit = mbit ? 1000 : 1024; - if (bytes < unit) - return bytes + (mbit ? " bit" : " B"); - - int exp = (int) (Math.log(bytes) / Math.log(unit)); - String pre = (mbit ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (mbit ? "" : ""); - if (mbit) - return String.format(Locale.getDefault(), "%.1f %sbit", bytes / Math.pow(unit, exp), pre); - else - return String.format(Locale.getDefault(), "%.1f %sB", bytes / Math.pow(unit, exp), pre); - } - - @Override - public IBinder onBind(Intent intent) { - String action = intent.getAction(); - if (action != null && action.equals(START_SERVICE)) - return mBinder; - else - return super.onBind(intent); - } - - @Override - public void onRevoke() { - mManagement.stopVPN(); - endVpnService(); - } - - // Similar to revoke but do not try to stop process - public void processDied() { - endVpnService(); - } - - private void endVpnService() { - synchronized (mProcessLock) { - mProcessThread = null; - } - VpnStatus.removeByteCountListener(this); - unregisterDeviceStateReceiver(); - ProfileManager.setConntectedVpnProfileDisconnected(this); - if (!mStarting) { - stopForeground(!mNotificationAlwaysVisible); - - if (!mNotificationAlwaysVisible) { - stopSelf(); - VpnStatus.removeStateListener(this); - } - } - } - - private void showNotification(String msg, String tickerText, boolean lowpriority, long when, ConnectionStatus status) { - String ns = Context.NOTIFICATION_SERVICE; - NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns); - - - int icon = getIconByConnectionStatus(status); - - android.app.Notification.Builder nbuilder = new Notification.Builder(this); - - if (mProfile != null) - nbuilder.setContentTitle(getString(R.string.notifcation_title, mProfile.mName)); - else - nbuilder.setContentTitle(getString(R.string.notifcation_title_notconnect)); - - nbuilder.setContentText(msg); - nbuilder.setOnlyAlertOnce(true); - nbuilder.setOngoing(true); - nbuilder.setContentIntent(getLogPendingIntent()); - nbuilder.setSmallIcon(icon); - - - if (when != 0) - nbuilder.setWhen(when); - - - // Try to set the priority available since API 16 (Jellybean) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) - jbNotificationExtras(lowpriority, nbuilder); - - if (tickerText != null && !tickerText.equals("")) - nbuilder.setTicker(tickerText); - - @SuppressWarnings("deprecation") - Notification notification = nbuilder.getNotification(); - - - mNotificationManager.notify(OPENVPN_STATUS, notification); - // startForeground(OPENVPN_STATUS, notification); - } - - private int getIconByConnectionStatus(ConnectionStatus level) { - switch (level) { - case LEVEL_CONNECTED: - return R.drawable.ic_stat_vpn; - case LEVEL_AUTH_FAILED: - case LEVEL_NONETWORK: - case LEVEL_NOTCONNECTED: - return R.drawable.ic_stat_vpn_offline; - case LEVEL_CONNECTING_NO_SERVER_REPLY_YET: - case LEVEL_WAITING_FOR_USER_INPUT: - return R.drawable.ic_stat_vpn_outline; - case LEVEL_CONNECTING_SERVER_REPLIED: - return R.drawable.ic_stat_vpn_empty_halo; - case LEVEL_VPNPAUSED: - return android.R.drawable.ic_media_pause; - case UNKNOWN_LEVEL: - default: - return R.drawable.ic_stat_vpn; - - } - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - private void jbNotificationExtras(boolean lowpriority, - android.app.Notification.Builder nbuilder) { - try { - if (lowpriority) { - Method setpriority = nbuilder.getClass().getMethod("setPriority", int.class); - // PRIORITY_MIN == -2 - setpriority.invoke(nbuilder, -2); - - Method setUsesChronometer = nbuilder.getClass().getMethod("setUsesChronometer", boolean.class); - setUsesChronometer.invoke(nbuilder, true); - - } - - Intent disconnectVPN = new Intent(this, DisconnectVPN.class); - disconnectVPN.setAction(DISCONNECT_VPN); - PendingIntent disconnectPendingIntent = PendingIntent.getActivity(this, 0, disconnectVPN, 0); - - nbuilder.addAction(android.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, - 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, - getString(R.string.resumevpn), resumeVPNPending); - } - - - //ignore exception - } catch (NoSuchMethodException nsm) { - VpnStatus.logException(nsm); - } catch (IllegalArgumentException e) { - VpnStatus.logException(e); - } catch (IllegalAccessException e) { - VpnStatus.logException(e); - } catch (InvocationTargetException e) { - VpnStatus.logException(e); - } - - } - - PendingIntent getLogPendingIntent() { - // Let the configure Button show the Log - Intent intent = new Intent(getBaseContext(), Dashboard.class); - intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - PendingIntent startLW = PendingIntent.getActivity(this, 0, intent, 0); - intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - return startLW; - - } - - synchronized void registerDeviceStateReceiver(OpenVPNManagement magnagement) { - // Registers BroadcastReceiver to track network connection changes. - IntentFilter filter = new IntentFilter(); - filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - filter.addAction(Intent.ACTION_SCREEN_OFF); - filter.addAction(Intent.ACTION_SCREEN_ON); - mDeviceStateReceiver = new DeviceStateReceiver(magnagement); - registerReceiver(mDeviceStateReceiver, filter); - VpnStatus.addByteCountListener(mDeviceStateReceiver); - } - - synchronized void unregisterDeviceStateReceiver() { - if (mDeviceStateReceiver != null) - try { - VpnStatus.removeByteCountListener(mDeviceStateReceiver); - this.unregisterReceiver(mDeviceStateReceiver); - } catch (IllegalArgumentException iae) { - // I don't know why this happens: - // java.lang.IllegalArgumentException: Receiver not registered: de.blinkt.openvpn.NetworkSateReceiver@41a61a10 - // Ignore for now ... - iae.printStackTrace(); - } - mDeviceStateReceiver = null; - } - - public void userPause(boolean shouldBePaused) { - if (mDeviceStateReceiver != null) - mDeviceStateReceiver.userPause(shouldBePaused); - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - - if (intent != null && intent.getBooleanExtra(ALWAYS_SHOW_NOTIFICATION, false)) - mNotificationAlwaysVisible = true; - - VpnStatus.addStateListener(this); - VpnStatus.addByteCountListener(this); - - if (intent != null && PAUSE_VPN.equals(intent.getAction())) { - if (mDeviceStateReceiver != null) - mDeviceStateReceiver.userPause(true); - return START_NOT_STICKY; - } - - if (intent != null && RESUME_VPN.equals(intent.getAction())) { - if (mDeviceStateReceiver != null) - mDeviceStateReceiver.userPause(false); - return START_NOT_STICKY; - } - - - if (intent != null && START_SERVICE.equals(intent.getAction())) - return START_NOT_STICKY; - if (intent != null && START_SERVICE_STICKY.equals(intent.getAction())) { - return START_REDELIVER_INTENT; - } - - if (intent == null) - return START_NOT_STICKY; - - // Extract information from the intent. - String prefix = getPackageName(); - String[] argv = intent.getStringArrayExtra(prefix + ".ARGV"); - String nativelibdir = intent.getStringExtra(prefix + ".nativelib"); - String profileUUID = intent.getStringExtra(prefix + ".profileUUID"); - - mProfile = ProfileManager.get(this, profileUUID); - - - String startTitle = getString(R.string.start_vpn_title, mProfile.mName); - String startTicker = getString(R.string.start_vpn_ticker, mProfile.mName); - showNotification(startTitle, startTicker, - false, 0, LEVEL_CONNECTING_NO_SERVER_REPLY_YET); - - // Set a flag that we are starting a new VPN - mStarting = true; - // Stop the previous session by interrupting the thread. - if (mManagement != null && mManagement.stopVPN()) - // an old was asked to exit, wait 1s - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - //ignore - } - - synchronized (mProcessLock) { - if (mProcessThread != null) { - mProcessThread.interrupt(); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - //ignore - } - } - } - // An old running VPN should now be exited - mStarting = false; - - // Start a new session by creating a new thread. - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - - mOvpn3 = prefs.getBoolean("ovpn3", false); - if (!"ovpn3".equals(BuildConfig.FLAVOR)) - mOvpn3 = false; - - - // Open the Management Interface - if (!mOvpn3) { - - // start a Thread that handles incoming messages of the managment socket - OpenVpnManagementThread ovpnManagementThread = new OpenVpnManagementThread(mProfile, this); - if (ovpnManagementThread.openManagementInterface(this)) { - - Thread mSocketManagerThread = new Thread(ovpnManagementThread, "OpenVPNManagementThread"); - mSocketManagerThread.start(); - mManagement = ovpnManagementThread; - VpnStatus.logInfo("started Socket Thread"); - } else { - return START_NOT_STICKY; - } - } - - - Runnable processThread; - if (mOvpn3) { - - OpenVPNManagement mOpenVPN3 = instantiateOpenVPN3Core(); - processThread = (Runnable) mOpenVPN3; - mManagement = mOpenVPN3; - - - } else { - HashMap env = new HashMap(); - processThread = new OpenVPNThread(this, argv, env, nativelibdir); - } - - synchronized (mProcessLock) { - mProcessThread = new Thread(processThread, "OpenVPNProcessThread"); - mProcessThread.start(); - } - if (mDeviceStateReceiver != null) - unregisterDeviceStateReceiver(); - - registerDeviceStateReceiver(mManagement); - - - ProfileManager.setConnectedVpnProfile(this, mProfile); - - return START_NOT_STICKY; - } - - private OpenVPNManagement instantiateOpenVPN3Core() { - try { - Class cl = Class.forName("de.blinkt.openvpn.core.OpenVPNThreadv3"); - return (OpenVPNManagement) cl.getConstructor(OpenVpnService.class,VpnProfile.class).newInstance(this,mProfile); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } catch (InstantiationException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - return null; - } - - @Override - public void onDestroy() { - synchronized (mProcessLock) { - if (mProcessThread != null) { - mManagement.stopVPN(); - } - } - - if (mDeviceStateReceiver != null) { - this.unregisterReceiver(mDeviceStateReceiver); - } - // Just in case unregister for state - VpnStatus.removeStateListener(this); - - } - - private String getTunConfigString() { - // The format of the string is not important, only that - // two identical configurations produce the same result - String cfg = "TUNCFG UNQIUE STRING ips:"; - - if (mLocalIP != null) - cfg += mLocalIP.toString(); - if (mLocalIPv6 != null) - cfg += mLocalIPv6; - - cfg += "routes: " + TextUtils.join("|", mRoutes.getNetworks(true)) + TextUtils.join("|", mRoutesv6.getNetworks(true)); - cfg += "excl. routes:" + TextUtils.join("|", mRoutes.getNetworks(false)) + TextUtils.join("|", mRoutesv6.getNetworks(false)); - cfg += "dns: " + TextUtils.join("|", mDnslist); - cfg += "domain: " + mDomain; - cfg += "mtu: " + mMtu; - return cfg; - } - - public ParcelFileDescriptor openTun() { - - //Debug.startMethodTracing(getExternalFilesDir(null).toString() + "/opentun.trace", 40* 1024 * 1024); - - Builder builder = new Builder(); - - VpnStatus.logInfo(R.string.last_openvpn_tun_config); - - - if (mLocalIP == null && mLocalIPv6 == null) { - VpnStatus.logError(getString(R.string.opentun_no_ipaddr)); - return null; - } - - if (mLocalIP != null) { - try { - builder.addAddress(mLocalIP.mIp, mLocalIP.len); - } catch (IllegalArgumentException iae) { - VpnStatus.logError(R.string.dns_add_error, mLocalIP, iae.getLocalizedMessage()); - return null; - } - } - - if (mLocalIPv6 != null) { - String[] ipv6parts = mLocalIPv6.split("/"); - try { - builder.addAddress(ipv6parts[0], Integer.parseInt(ipv6parts[1])); - } catch (IllegalArgumentException iae) { - VpnStatus.logError(R.string.ip_add_error, mLocalIPv6, iae.getLocalizedMessage()); - return null; - } - - } - - - for (String dns : mDnslist) { - try { - builder.addDnsServer(dns); - } catch (IllegalArgumentException iae) { - VpnStatus.logError(R.string.dns_add_error, dns, iae.getLocalizedMessage()); - } - } - - - builder.setMtu(mMtu); - - Collection positiveIPv4Routes = mRoutes.getPositiveIPList(); - Collection positiveIPv6Routes = mRoutesv6.getPositiveIPList(); - - for (NetworkSpace.ipAddress route : positiveIPv4Routes) { - try { - builder.addRoute(route.getIPv4Address(), route.networkMask); - } catch (IllegalArgumentException ia) { - VpnStatus.logError(getString(R.string.route_rejected) + route + " " + ia.getLocalizedMessage()); - } - } - - for (NetworkSpace.ipAddress route6 : positiveIPv6Routes) { - try { - builder.addRoute(route6.getIPv6Address(), route6.networkMask); - } catch (IllegalArgumentException ia) { - VpnStatus.logError(getString(R.string.route_rejected) + route6 + " " + ia.getLocalizedMessage()); - } - } - - if (mDomain != null) - builder.addSearchDomain(mDomain); - - VpnStatus.logInfo(R.string.local_ip_info, mLocalIP.mIp, mLocalIP.len, mLocalIPv6, mMtu); - VpnStatus.logInfo(R.string.dns_server_info, TextUtils.join(", ", mDnslist), mDomain); - VpnStatus.logInfo(R.string.routes_info_incl, TextUtils.join(", ", mRoutes.getNetworks(true)), TextUtils.join(", ", mRoutesv6.getNetworks(true))); - VpnStatus.logInfo(R.string.routes_info_excl, TextUtils.join(", ", mRoutes.getNetworks(false)),TextUtils.join(", ", mRoutesv6.getNetworks(false))); - VpnStatus.logDebug(R.string.routes_debug, TextUtils.join(", ", positiveIPv4Routes), TextUtils.join(", ", positiveIPv6Routes)); - - String session = mProfile.mName; - if (mLocalIP != null && mLocalIPv6 != null) - session = getString(R.string.session_ipv6string, session, mLocalIP, mLocalIPv6); - else if (mLocalIP != null) - session = getString(R.string.session_ipv4string, session, mLocalIP); - - builder.setSession(session); - - // No DNS Server, log a warning - if (mDnslist.size() == 0) - VpnStatus.logInfo(R.string.warn_no_dns); - - mLastTunCfg = getTunConfigString(); - - // Reset information - mDnslist.clear(); - mRoutes.clear(); - mRoutesv6.clear(); - mLocalIP = null; - mLocalIPv6 = null; - mDomain = null; - - builder.setConfigureIntent(getLogPendingIntent()); - - try { - //Debug.stopMethodTracing(); - return builder.establish(); - } catch (Exception e) { - VpnStatus.logError(R.string.tun_open_error); - VpnStatus.logError(getString(R.string.error) + e.getLocalizedMessage()); - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR1) { - VpnStatus.logError(R.string.tun_error_helpful); - } - return null; - } - - } - - public void addDNS(String dns) { - mDnslist.add(dns); - } - - public void setDomain(String domain) { - if (mDomain == null) { - mDomain = domain; - } - } - - /** Route that is always included, used by the v3 core */ - public void addRoute (CIDRIP route) { - mRoutes.addIP(route, true); - } - - public void addRoute (String dest, String mask, String gateway, String device) { - CIDRIP route = new CIDRIP(dest, mask); - boolean include = isAndroidTunDevice(device); - - NetworkSpace.ipAddress gatewayIP = new NetworkSpace.ipAddress(new CIDRIP(gateway, 32),false); - - if (mLocalIP==null) { - VpnStatus.logError("Local IP address unset but adding route?! This is broken! Please contact author with log"); - return; - } - NetworkSpace.ipAddress localNet = new NetworkSpace.ipAddress(mLocalIP,true); - if (localNet.containsNet(gatewayIP)) - include=true; - - if (gateway!= null && - (gateway.equals("255.255.255.255") || gateway.equals(mRemoteGW))) - include=true; - - - if (route.len == 32 && !mask.equals("255.255.255.255")) { - VpnStatus.logWarning(R.string.route_not_cidr, dest, mask); - } - - if (route.normalise()) - VpnStatus.logWarning(R.string.route_not_netip, dest, route.len, route.mIp); - - mRoutes.addIP(route, include); - } - - public void addRoutev6(String network, String device) { - String[] v6parts = network.split("/"); - boolean included = isAndroidTunDevice(device); - - // Tun is opened after ROUTE6, no device name may be present - - try { - Inet6Address ip = (Inet6Address) InetAddress.getAllByName(v6parts[0])[0]; - int mask = Integer.parseInt(v6parts[1]); - mRoutesv6.addIPv6(ip, mask, included); - - } catch (UnknownHostException e) { - VpnStatus.logException(e); - } - - - } - - private boolean isAndroidTunDevice(String device) { - return device!=null && - (device.startsWith("tun") || "(null)".equals(device) || "vpnservice-tun".equals(device)); - } - - public void setMtu(int mtu) { - mMtu = mtu; - } - - public void setLocalIP(CIDRIP cdrip) { - mLocalIP = cdrip; - } - - public void setLocalIP(String local, String netmask, int mtu, String mode) { - mLocalIP = new CIDRIP(local, netmask); - mMtu = mtu; - mRemoteGW=null; - - long netMaskAsInt = CIDRIP.getInt(netmask); - - if (mLocalIP.len == 32 && !netmask.equals("255.255.255.255")) { - // get the netmask as IP - - int masklen; - if ("net30".equals(mode)) - masklen = 30; - else - masklen = 31; - - int mask = ~( 1 << (32 - (mLocalIP.len +1))); - // Netmask is Ip address +/-1, assume net30/p2p with small net - if ((netMaskAsInt & mask) == (mLocalIP.getInt() & mask )) { - mLocalIP.len = masklen; - } else { - mLocalIP.len = 32; - if (!"p2p".equals(mode)) - VpnStatus.logWarning(R.string.ip_not_cidr, local, netmask, mode); - } - } - if (("p2p".equals(mode)) && mLocalIP.len < 32 || "net30".equals("net30") && mLocalIP.len < 30) { - VpnStatus.logWarning(R.string.ip_looks_like_subnet, local, netmask, mode); - } - - - // Configurations are sometimes really broken... - mRemoteGW=netmask; - } - - public void setLocalIPv6(String ipv6addr) { - mLocalIPv6 = ipv6addr; - } - - @Override - public void updateState(String state, String logmessage, int resid, ConnectionStatus level) { - // If the process is not running, ignore any state, - // Notification should be invisible in this state - doSendBroadcast(state, level); - if (mProcessThread == null && !mNotificationAlwaysVisible) - return; - - boolean lowpriority = false; - // Display byte count only after being connected - - { - if (level == LEVEL_WAITING_FOR_USER_INPUT) { - // The user is presented a dialog of some kind, no need to inform the user - // with a notifcation - return; - } else if (level == LEVEL_CONNECTED) { - mDisplayBytecount = true; - mConnecttime = System.currentTimeMillis(); - lowpriority = true; - NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - mNotificationManager.cancel(OPENVPN_STATUS); - } else if(!mProfile.mPersistTun || mConnecttime == 0){ - mDisplayBytecount = false; - String msg = getString(resid); - String ticker = msg; - showNotification(msg + " " + logmessage, ticker, lowpriority , 0, level); - } else if(mProfile.mPersistTun && level == LEVEL_NONETWORK) { - NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - mNotificationManager.cancel(OPENVPN_STATUS); - } else if(mProfile.mPersistTun && mConnecttime > 0) { - mDisplayBytecount = false; - String msg = "Traffic is blocked until the VPN becomes active."; - String ticker = msg; - showNotification(msg, ticker, lowpriority , 0, level); - } - - } - } - - private void doSendBroadcast(String state, ConnectionStatus level) { - Intent vpnstatus = new Intent(); - vpnstatus.setAction("de.blinkt.openvpn.VPN_STATUS"); - vpnstatus.putExtra("status", level.toString()); - vpnstatus.putExtra("detailstatus", state); - sendBroadcast(vpnstatus, permission.ACCESS_NETWORK_STATE); - } - - @Override - public void updateByteCount(long in, long out, long diffIn, long diffOut) { - if (mDisplayBytecount) { - String netstat = String.format(getString(R.string.statusline_bytecount), - humanReadableByteCount(in, false), - humanReadableByteCount(diffIn / OpenVPNManagement.mBytecountInterval, true), - humanReadableByteCount(out, false), - humanReadableByteCount(diffOut / OpenVPNManagement.mBytecountInterval, true)); - } - - } - - @Override - public boolean handleMessage(Message msg) { - Runnable r = msg.getCallback(); - if (r != null) { - r.run(); - return true; - } else { - return false; - } - } - - public OpenVPNManagement getManagement() { - return mManagement; - } - - public String getTunReopenStatus() { - String currentConfiguration = getTunConfigString(); - if (currentConfiguration.equals(mLastTunCfg)) { - return "NOACTION"; - } else { - String release = Build.VERSION.RELEASE; - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT && !release.startsWith("4.4.3") - && !release.startsWith("4.4.4") && !release.startsWith("4.4.5") && !release.startsWith("4.4.6")) - // There will be probably no 4.4.4 or 4.4.5 version, so don't waste effort to do parsing here - return "OPEN_AFTER_CLOSE"; - else - return "OPEN_BEFORE_CLOSE"; - } - } - - public class LocalBinder extends Binder { - public OpenVpnService getService() { - // Return this instance of LocalService so clients can call public methods - return OpenVpnService.this; - } - } -} diff --git a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java index d96a66a0..41879103 100644 --- a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java +++ b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java @@ -51,7 +51,7 @@ import se.leap.bitmaskclient.R; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.activities.DisconnectVPN; import de.blinkt.openvpn.core.OpenVPNManagement; -import de.blinkt.openvpn.core.OpenVpnService; +import de.blinkt.openvpn.core.OpenVPNService; import de.blinkt.openvpn.core.ProfileManager; import de.blinkt.openvpn.core.VpnStatus; import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus; @@ -59,7 +59,7 @@ import de.blinkt.openvpn.core.VpnStatus.LogItem; import de.blinkt.openvpn.core.VpnStatus.LogListener; import de.blinkt.openvpn.core.VpnStatus.StateListener; -import static de.blinkt.openvpn.core.OpenVpnService.humanReadableByteCount; +import static de.blinkt.openvpn.core.OpenVPNService.humanReadableByteCount; import se.leap.bitmaskclient.Dashboard; @@ -501,8 +501,8 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar. super.onResume(); VpnStatus.addStateListener(this); VpnStatus.addByteCountListener(this); - Intent intent = new Intent(getActivity(), OpenVpnService.class); - intent.setAction(OpenVpnService.START_SERVICE); + Intent intent = new Intent(getActivity(), OpenVPNService.class); + intent.setAction(OpenVPNService.START_SERVICE); } diff --git a/ics-openvpn-stripped/main/src/main/res/values-ca/strings.xml b/ics-openvpn-stripped/main/src/main/res/values-ca/strings.xml index 554b4816..c90277d7 100755 --- a/ics-openvpn-stripped/main/src/main/res/values-ca/strings.xml +++ b/ics-openvpn-stripped/main/src/main/res/values-ca/strings.xml @@ -1,5 +1,5 @@ - + OpenVPN per Android diff --git a/ics-openvpn-stripped/main/src/main/res/values-de/strings.xml b/ics-openvpn-stripped/main/src/main/res/values-de/strings.xml index eb6ef36e..bdda9bb2 100755 --- a/ics-openvpn-stripped/main/src/main/res/values-de/strings.xml +++ b/ics-openvpn-stripped/main/src/main/res/values-de/strings.xml @@ -1,5 +1,5 @@ - + OpenVPN für Android @@ -156,7 +156,7 @@ Importiertes Profil Importiertes Profil %d Fehlerhafte Images - <p>Von offiziellen HTC Firmwares ist bekannt, dass diese teilweise merkwürdige Routing Probleme haben, die dafür sorgen, dass der Verkehr nicht durch den Tunnel fließt. (Siehe auch <a href=\"http://code.google.com/p/ics-openvpn/issues/detail?id=18\">Issue 18</a> im Bug Tracker.)</p><p>Bei älteren Version der offiziellen SONY Firmwares für das Xperia arc S and Xperia Ray scheint der VPNService Support komplett zu fehlen. (Siehe auch <a href=\"http://code.google.com/p/ics-openvpn/issues/detail?id=29\">Issue 29</a> im Bug Tracker.)</p><p>Auf anderen (insbesondere Custom ROMS) fehlt teilweise das tun Kernel Modul oder die Rechte von /dev/tun sind falsch gesetzt. Auf einigen CM9 Firmware wird die \"/dev/tun Eigentümer setzen\" Option in den Allgemeinen Einstellungen benötigt.</p><p>Am wichtigsten ist aber, falls Sie eine fehlerhafte Firmware haben, melden Sie dies Ihrem Hersteller. Desto mehr Leute den Fehler dem Hersteller melden desto wahrscheinlicher werden Sie eine Fehlerkorrektur bekommen.</p> + <p>Von offiziellen HTC Firmwares ist bekannt, dass diese teilweise merkwürdige Routing Probleme haben, die dafür sorgen, dass der Verkehr nicht durch den Tunnel fließt. (Siehe auch <a href=\"http://code.google.com/p/ics-openvpn/issues/detail?id=18\">Issue 18</a> im Bug Tracker.)</p><p>Bei älteren Version der offiziellen SONY Firmwares für das Xperia arc S and Xperia Ray scheint der VPNService Support komplett zu fehlen. (Siehe auch <a href=\"http://code.google.com/p/ics-openvpn/issues/detail?id=29\">Issue 29</a> im Bug Tracker.)</p><p>Auf anderen (insbesondere Custom ROMS) fehlt teilweise das tun Kernel Modul oder die Rechte von /dev/tun sind falsch gesetzt. Auf einigen CM9 Firmware wird die \"/dev/tun Eigentümer setzen\" Option in den Allgemeinen Einstellungen benötigt.</p><p>Am wichtigsten ist aber, falls Sie eine fehlerhafte Firmware haben, melden Sie dies Ihrem Hersteller. Je mehr Leute den Fehler dem Hersteller melden, desto wahrscheinlicher werden Sie eine Fehlerkorrektur bekommen.</p> PKCS12 Veschlüsslungspassword Passphrase privater Schlüssel Passwort diff --git a/ics-openvpn-stripped/main/src/main/res/values-es/strings.xml b/ics-openvpn-stripped/main/src/main/res/values-es/strings.xml index 5ad33bbd..ffd37c5f 100755 --- a/ics-openvpn-stripped/main/src/main/res/values-es/strings.xml +++ b/ics-openvpn-stripped/main/src/main/res/values-es/strings.xml @@ -1,5 +1,5 @@ - + OpenVPN para Android @@ -215,6 +215,7 @@ Use el icono <img src=\"ic_menu_archive\"/> para importar un perfil existente (.ovpn or .conf) de tu tarjeta. Asegúrese de checar también las preguntas frecuentes. Hay una guía de inicio rápido. Configuración de enrutamiento o interfaz + El enrutamiento y la configuración de la interfaz no se realiza a través de comandos tradicionales ifconfig / ruta, pero mediante el uso de la API VPNService. Esto resulta en una configuración de enrutamiento diferente que en otros sistemas operativos. La configuración del túnel VPN consta de la dirección IP y las redes que deben ser colocados de través de esta interfaz. Se necesita Especialmente hay dirección compañero de estudios o de gateway. Rutas especiales para llegar a la VPN Server (por ejemplo agregan al usar redirect-gateway) no son necesarios, ya sea. La aplicación, en consecuencia ignorará esta configuración al importar una configuración. La aplicación asegura con la API VPNService que la conexión con el servidor no se encamina a través del túnel VPN. Sólo redes especificando ser enrutados a través del túnel es compatible. La aplicación intenta detectar las redes que no deben ser enrutados a través de túnel (por ejemplo, la ruta xxxx aaaa net_gateway) y calcula un conjunto de rutas que excluye este rutas para emular el comportamiento de otras plataformas. Las ventanas de registro muestra la configuración de la VPNService al establecer una conexión. No regresar a modo sin conexión VPN cuando OpenVPN esta volviendose a conectar. Tun persistente Registro de OpenVPN @@ -314,4 +315,8 @@ Las redes conectadas directamente a los interfaces locales no serán enrutadas a través de la VPN. Al desmarcar esta opción, todo el tráfico previsto para las redes locales será redirigido a la VPN. Evitar la VPN para las redes locales Archivo de Usuario/Contraseña + [Importado de:%s] + Algunos archivos no se pudo encontrar. Por favor, seleccione los archivos que desea importar el perfil: + Para utilizar esta aplicación usted necesita un proveedor de servicio VPN / es un apoyo OpenVPN (a menudo proporcionados por su empleador). Echa un vistazo a http://community.openvpn.net/ para más información sobre OpenVPN y cómo configurar su propio servidor OpenVPN. + Importar registros: diff --git a/ics-openvpn-stripped/main/src/main/res/values-fr/strings.xml b/ics-openvpn-stripped/main/src/main/res/values-fr/strings.xml index 8479d591..626ea89c 100755 --- a/ics-openvpn-stripped/main/src/main/res/values-fr/strings.xml +++ b/ics-openvpn-stripped/main/src/main/res/values-fr/strings.xml @@ -1,5 +1,5 @@ - + "OpenVPN pour Android" @@ -216,6 +216,7 @@ Sur certaines images, cette notification joue un son.\nAndroid à introduit ces "Utilisez l\'icône <img src=\"ic_menu_archive\"/> pour importer un fichier profil (.opvpn ou .conf) de votre carte SD." "Veillez également à consulter la FAQ. Il s\'y trouve un guide de démarrage rapide." "Redirections / Configuration de l\'interface" + The Routing and interface configuration is not done via traditional ifconfig/route commands but by using the VPNService API. This results in a different routing configuration than on other OSes. The configuration for the VPN tunnel consists of the IP address and the networks that should be routed over this interface. Especially no peer partner address or gateway address is needed. Special routes to reach the VPN Server (for example added when using redirect-gateway) are not needed either. The application will consequently ignore these settings when importing a configuration. The app ensures with the VPNService API that the connection to the server is not routed through the VPN tunnel. Only specifying networks to be routed via tunnel is supported. The app tries to detect networks that should not be routed over tunnel (e.g. route x.x.x.x y.y.y.y net_gateway) and calculates a route set that excludes this routes to emulate the behaviour of other platforms. The log windows shows the configuration of the VPNService upon establishing a connection. Ne pas couper la connexion VPN lors de la reconnexion d\'OpenVPN. Persistance de l\'interface TUN Log OpenVPN diff --git a/ics-openvpn-stripped/main/src/main/res/values-ja/strings.xml b/ics-openvpn-stripped/main/src/main/res/values-ja/strings.xml index fddc6dea..05525a4b 100755 --- a/ics-openvpn-stripped/main/src/main/res/values-ja/strings.xml +++ b/ics-openvpn-stripped/main/src/main/res/values-ja/strings.xml @@ -1,5 +1,5 @@ - + OpenVPN for Android diff --git a/ics-openvpn-stripped/main/src/main/res/values-pt/strings.xml b/ics-openvpn-stripped/main/src/main/res/values-pt/strings.xml new file mode 100755 index 00000000..9a9138f3 --- /dev/null +++ b/ics-openvpn-stripped/main/src/main/res/values-pt/strings.xml @@ -0,0 +1,139 @@ + + + + + OpenVPN para Android + Endereço do Servidor: + Porta: + Localização + Não foi possível ler o diretório + Selecionar + Cancelar + Não há dados + Compressão LZO + Sem Certificado + Certificado do Cliente + Chave do Certificado do Cliente + Arquivo PKCS12 + Certificado CA + Você deve selecionar um certificado + O código fonte e o rastreamento de incidentes estão disponíveis em http://code.google.com/p/ics-openvpn/ + Este programa utiliza os seguintes componentes; veja o código fonte para mais detalhes das licenças + Sobre + Perfis + Tipo + Senha do PKCS12 + Selecionar… + Você deve selecionar um arquivo + Utilizar Autenticação TLS + Direção TLS + Entre o Endereço IPv6/CIDR (ex: 2000:dd::23/64) + Entre o endereço IPv4/CIDR (ex: 1.2.3.4/24) + Endereço IPv4 + Endereço IPv6 + Insira as opções personalizadas para o OpenVPN. Utilize com cuidado. Observe também que muitas das opções relacionadas ao tun do OpenVPN não podem ser suportadas pelo design do VPNSettings. Contate o autor se você acha que uma opção importante está faltando. + Usuário + Senha + Para a configuração estática as chaves de autenticação de TLS serão utilizadas como chaves estáticas + Configurar VPN + Adicionar Perfil + Digite um nome que identifica o novo perfil + Por favor, digite um nome de perfil único + Nome do perfil + Você deve selecionar um certificado de usuário + Nenhum erro encontrado + Erro na configuração + Erro ao analisar o endereço IPv4 + Erro ao analisar as rotas personalizadas + (deixe em branco para consulta sob demanda) + Atalho do OpenVPN + Conectar a VPN + O perfil especificado no atalho não foi encontrado + Prefixo de Host aleatório + Adiciona 6 caracteres aleatórios na frente do hostname + Habilitar opções personalizadas + Opções personalizadas. Use com cuidado! + Rota rejeitada pelo Android + Desconectar + Desconectar VPN + limpar log + Cancelar confirmação + Desconectar a VPN conectada/cancelar a tentativa de conexão? + Remover VPN + Verifica se o servidor usa um certificado com as extensões de servidor TLS (- servidor remoto-cert-TLS) + Esperar certificado do servidor TLS + Verifica o DN Subject do certificado do servidor remoto + Verificar o Hostname do Certificado + Especificar a conta usada para verificar o certificado remoto DN (por exemplo, C = DE, L = Paderborn, UO = aviária operadoras IP, CN=openvpn.blinkt.de)\n\Especificar o DN completo ou o RDN (openvpn.blinkt.de no exemplo) ou um prefixo RDN para verification.\n\nWhen usando o prefixo RDN \"Servidor\" corresponde a \"Server-1\" e \"Server-2\" \n\nDeixando vazio, o campo de texto irá verificar o RDN contra o servidor hostname.\n\nPara mais detalhes consulte a página principal do 2.3.1+ OpenVPN sob — verificar-X509-nome + Subject do certificado remoto + Permite a Autenticação de Chave TLS + Arquivo de Auth TLS + Solicitações de endereços de IP, rotas e opções de sincronização do servidor. + Nenhuma informação é solicitada do servidor. Configurações precisam ser especificadas abaixo. + Obter Configurações + DNS + Substituir as configurações de DNS pelo servidor + Use seus próprios servidores de DNS + Domínio de pesquisa + Servidor DNS a ser usado. + Servidor DNS + Servidor DNS secundário utilizado caso o servidor primário esteja inacessível. + Servidor DNS alternativo + Ignorar rotas empurradas + Ignorar rota empurrada pelo servidor. + Redireccionar todo o tráfego pela VPN + Usar rota padrão + Digite rotas personalizadas. Apenas indique destino em formato CIDR. \"10.0.0.0 / 8 2002 :: / 16\" iria dirigir as redes 10.0.0.0 / 8 e 2002 :: / 16 sobre a VPN. + As rotas que não devem ser encaminhados pelo VPN. Use a mesma sintaxe para rotas incluídas. + Rotas personalizadas + Redes excluídas + Nível de complexidade do log + Permite pacotes autenticados a partir de qualquer IP + Permitir servidor flutuante + Opções personalizadas + Editar configurações de VPN + Remover o perfil VPN \'%s\'? + Em algumas imagens ICS personalizado a permissão em / dev / tun pode estar errada, ou o módulo tun pode estar faltando completamente. Para imagens CM9 tente a opção correção propriedade sobre as configurações gerais + Falha ao abrir a interface de tun + "Erro:" + Claro + Abrindo a interface tun: + Local IPv4: %1$s/%2$d IPv6:%3$s MTU:%4$d + Servidor DNS: %1$s, domínio: %2$s + Rotas: %1$s %2$s + Rotas excluídas: %1$s %2$s + Rotas VpnService instaladas: %1$s %2$s + Existem múltiplas informações de interface, %1$s e %2$s, a aplicação assume que o segundo endereço é um endereço \'peer\' do endereço remoto. Será usada uma máscara de rede /32 para o IP local. O modo estabelecido pela OpenVPN é \"%3$s\". + Não consigo entender %1$s e %2$s como uma rota IP com máscara de rede CIDR, usando /32 como máscara de rede. + A rota %1$s/%2$s foi corrigida para %3$s/%2$s + Não é possível aceder aos certificados \'Keychain Android\'. Isso pode ter sido causado por uma atualização de firmware ou uma restauração das configurações da app/app. Será necessário editar o perfil VPN e selecionar novamente o certificado nas configurações básicas para recriar a permissão e possibilitar o acesso ao certificado. + %1$s %2$s + Enviar arquivo de log + Enviar + Ficheiro de registo do ICS OpenVPN + Entrada de registo copiada para a área de transferência + Entrada de registo copiada para a área de transferência + A API VPN não permite o modo Tap em dispositivos sem acesso root. Desta forma não é possível oferecer suporte Tap nesta aplicação + Novamente? Você está brincando? Não, o modo tap não é suportado de maneira nenhuma e enviar mais emails a perguntar se eventualmente será, não irá ajudar. + Perguntas frequentes + Encriptação + Importar + Importar + IPv4 + IPv6 + Senha + Configurações + Avançado + Ignorar + Reiniciar + Conectando + Autenticando + Conectado + Add + Pausa VPN + Retomar VPN + Upload + Download + Vpn Status + Ver opções + diff --git a/ics-openvpn-stripped/main/src/main/res/values-ru/strings.xml b/ics-openvpn-stripped/main/src/main/res/values-ru/strings.xml index 6747bcab..13390642 100755 --- a/ics-openvpn-stripped/main/src/main/res/values-ru/strings.xml +++ b/ics-openvpn-stripped/main/src/main/res/values-ru/strings.xml @@ -1,5 +1,5 @@ - + OpenVPN для Android @@ -215,6 +215,7 @@ Используйте <img src=\"ic_menu_archive\"/> кнопку для импорта существующих профилей (.ovpn or .conf) с вашей карты памяти. Не забудьте заглянуть в FAQ. Также имеется краткое руководство. Конфигурация маршрутизации/интерфейса + Конфигурация маршрутизации и интерфейса производится не через традиционные ifconfig/route команды, а с помощью VPNService API. Это приводит к созданию другой конфигурации маршрутизации, отличной от конфигураций, используемых на других ОС. Конфигурация VPN-туннеля состоит из IP-адресов и сетей, которые должны направляться через этот интерфейс. Никаких особых партнерских адресов или адресов шлюза не требуется. Также не требуются и специальные маршруты для соединения с VPN-сервером (например, добавленные при использовании redirect-gateway). Следовательно, приложение будет игнорировать эти параметры при импорте конфигурации. Приложение с помощью VPNService API гарантирует, что подключение к серверу не направляется через VPN-туннель. Поддерживается направление через туннель только определенных сетей. Приложение пытается определить сети, которые не должны быть направлены через туннель (например, маршрут x.x.x.x y.y.y.y net_gateway) и вычисляет список маршрутов, в который не включаются эти маршруты, чтобы эмулировать поведение других платформ. Окна журналов и логов показывают конфигурацию сервиса VPN после установления соединения. Не открывать диалог, когда происходит переподключение VPN. Постоянный tun OpenVPN Журнал @@ -291,6 +292,8 @@ Поведение VPN Разрешить изменение VPN-профилей Аппаратное хранилище ключей: + Иконка приложения пытается использовать OpenVPN для Android + «Начиная с Android 4.3, диалог подтверждения VPN-соединения защищен от приложений, \"накладывающихся поверх экрана». Это приводит к тому, что диалоговое окно подтверждения не реагирует на сенсорные нажатия. Если у вас имеется приложение, использующее наложения, то это может вызвать такое поведение. Если вы обнаружите где-либо такое приложение, свяжитесь с автором приложения. Эта проблема затрагивает все VPN приложения на Android 4.3 и более поздних версиях. Смотрите также < a href = \"http://code.google.com/p/ics-openvpn/issues/detail?id=185\" > Проблему 185 < > для получения дополнительных сведений» Окно подтверждения VPN для Android 4.3 и позже Также Вы можете выразить благодарность в виде пожертвования на Play Store: Спасибо за пожертвование %s! @@ -308,8 +311,10 @@ Неопознання ошибка: %1$s\n\n%2$s %3$s: %1$s\n\n%2$s Если на Вашем устройстве установлены Рут права, Вы можете установить <a href=\"http://xposed.info/\">Xposed framework</a> и <a href=\"http://repo.xposed.info/module/de.blinkt.vpndialogxposed\">модуль автоматического подтверждения диалога подключения VPN</a> на свой страх и риск + Полные тексты лицензий Сети напрямую доступные через локальный интерфейс не будут маршрутизированы через VPN. Отключите эту опцию чтобы направить трафик через VPN. Не использовать VPN для локальных адресов + Файл логина и пароля [Импортировано из: %s] Некоторые файлы не найдены. Выберите файлы для импорта в профиль: Для использования данного приложения Вам необходим VPN провайдер/шлюз поддерживающий OpenVPN. Для получения информации по настройке собственного OpenVPN сервера: http://community.openvpn.net/ diff --git a/ics-openvpn-stripped/main/src/main/res/values/strings.xml b/ics-openvpn-stripped/main/src/main/res/values/strings.xml index a054d16b..8b302ad7 100755 --- a/ics-openvpn-stripped/main/src/main/res/values/strings.xml +++ b/ics-openvpn-stripped/main/src/main/res/values/strings.xml @@ -254,7 +254,7 @@ Connecting to VPN %s Some versions of Android 4.1 have problems if the name of the keystore certificate contains non alphanumeric characters (like spaces, underscores or dashes). Try to reimport the certificate without special characters Encryption cipher - Packets authentication + Packet authentication Enter packet authentication method Running on %1$s (%2$s) %3$s, Android API %4$d, version %5$s, %6$s built by %s @@ -319,4 +319,9 @@ To use this app you need a VPN provider/VPN gateway supporting OpenVPN (often provided by your employer). Check out http://community.openvpn.net/ for more information on OpenVPN and how to setup your own OpenVPN server. Import log: Vpn topology \"%3$s\" specified but ifconfig %1$s %2$s looks more like an IP address with a network mask. Assuming \"subnet\" topology. + mssfix value has to be a integer between 0 and 9000 + Announce to TCP sessions running over the tunnel that they should limit their send packet sizes such that after OpenVPN has encapsulated them, the resulting UDP packet size that OpenVPN sends to its peer will not exceed this number of bytes. (default is 1450) + Override MSS value of TCP payload + Set MSS of TCP payload + diff --git a/ics-openvpn-stripped/main/src/main/res/values/untranslatable.xml b/ics-openvpn-stripped/main/src/main/res/values/untranslatable.xml index 4d2bc4d9..6845f984 100644 --- a/ics-openvpn-stripped/main/src/main/res/values/untranslatable.xml +++ b/ics-openvpn-stripped/main/src/main/res/values/untranslatable.xml @@ -34,11 +34,12 @@ 50 -1 - + OpenVPN for Android crashed, crash reported + Waiting for user permission to use VPN API Waiting for user VPN password VPN password input dialog cancelled VPN API permission dialog cancelled - - \ No newline at end of file + + diff --git a/ics-openvpn-stripped/main/src/ovpn3/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java b/ics-openvpn-stripped/main/src/ovpn3/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java index 6d3dce69..e595106c 100644 --- a/ics-openvpn-stripped/main/src/ovpn3/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java +++ b/ics-openvpn-stripped/main/src/ovpn3/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java @@ -14,9 +14,7 @@ import net.openvpn.ovpn3.ClientAPI_TransportStats; import java.lang.Override; import de.blinkt.openvpn.VpnProfile; -import de.blinkt.openvpn.core.OpenVPNManagement; -import de.blinkt.openvpn.core.OpenVpnService; -import de.blinkt.openvpn.core.VpnStatus; + import android.content.Context; public class OpenVPNThreadv3 extends ClientAPI_OpenVPNClient implements Runnable, OpenVPNManagement { @@ -29,7 +27,7 @@ public class OpenVPNThreadv3 extends ClientAPI_OpenVPNClient implements Runnable } private VpnProfile mVp; - private OpenVpnService mService; + private OpenVPNService mService; class StatusPoller implements Runnable { @@ -224,7 +222,7 @@ public class OpenVPNThreadv3 extends ClientAPI_OpenVPNClient implements Runnable } - public OpenVPNThreadv3(OpenVpnService openVpnService, VpnProfile vp) { + public OpenVPNThreadv3(OpenVPNService openVpnService, VpnProfile vp) { init_process(); mVp =vp; mService =openVpnService; -- cgit v1.2.3