summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorParménides GV <parmegv@sdf.org>2014-10-08 20:59:30 +0200
committerParménides GV <parmegv@sdf.org>2014-10-08 20:59:30 +0200
commit2b56dd61c0bd9eb6f71405d1d7f07f3051b29601 (patch)
tree7f4f30989d99f743467b3c382ff4858f45d1eead
parent34643c6b5ab0643383e24025876b0d69859ba4f9 (diff)
parent35674aa6be80c153a0fbe4a4c15c54e02aef0b3e (diff)
Merge branch 'feature/Early-routes-#5816' into develop
-rw-r--r--app/src/main/AndroidManifest.xml10
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java5
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EIP.java307
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/VoidVpnLauncher.java37
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/VoidVpnService.java42
5 files changed, 255 insertions, 146 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 423293b6..68a99560 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -36,6 +36,14 @@
android:label="@string/app" >
<service
+ android:name="se.leap.bitmaskclient.VoidVpnService"
+ android:permission="android.permission.BIND_VPN_SERVICE">
+ <intent-filter>
+ <action android:name="android.net.VpnService" />
+ </intent-filter>
+ </service>
+
+ <service
android:name="de.blinkt.openvpn.core.OpenVPNService"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
@@ -53,6 +61,8 @@
</intent-filter>
</receiver>
+ <activity
+ android:name="se.leap.bitmaskclient.VoidVpnLauncher" />
<activity
android:theme="@android:style/Theme.DeviceDefault.Light.Dialog"
android:name="de.blinkt.openvpn.activities.DisconnectVPN" />
diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java
index a8bd3b7a..c95d0c8b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java
@@ -18,8 +18,9 @@ package se.leap.bitmaskclient;
import java.io.ByteArrayInputStream;
import java.io.IOException;
-import java.math.BigInteger;
import java.io.InputStream;
+import java.math.BigInteger;
+import java.lang.IllegalArgumentException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
@@ -105,6 +106,8 @@ public class ConfigHelper {
e.printStackTrace();
} catch (IOException e) {
return null;
+ } catch (IllegalArgumentException e) {
+ return null;
}
return (X509Certificate) certificate;
diff --git a/app/src/main/java/se/leap/bitmaskclient/EIP.java b/app/src/main/java/se/leap/bitmaskclient/EIP.java
index 43fe0b7c..4a8bae46 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EIP.java
@@ -16,35 +16,23 @@
*/
package se.leap.bitmaskclient;
-
-
-
-
import android.app.Activity;
import android.app.IntentService;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
import android.content.SharedPreferences;
-import android.drm.DrmStore.Action;
import android.os.Bundle;
-import android.os.IBinder;
import android.os.ResultReceiver;
import android.util.Log;
import de.blinkt.openvpn.LaunchVPN;
import de.blinkt.openvpn.VpnProfile;
import de.blinkt.openvpn.activities.DisconnectVPN;
-import de.blinkt.openvpn.core.ConfigParser.ConfigParseError;
import de.blinkt.openvpn.core.ConfigParser;
-import de.blinkt.openvpn.core.OpenVpnManagementThread;
-import de.blinkt.openvpn.core.OpenVPNService.LocalBinder;
-import de.blinkt.openvpn.core.OpenVPNService;
+import de.blinkt.openvpn.core.ConfigParser.ConfigParseError;
import de.blinkt.openvpn.core.ProfileManager;
import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus;
import java.io.IOException;
import java.io.StringReader;
-import java.lang.StringBuffer;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
@@ -106,8 +94,6 @@ public final class EIP extends IntentService {
private static Context context;
private static ResultReceiver mReceiver;
private static boolean mBound = false;
- // Used to store actions to "resume" onServiceConnection
- private static String mPending = null;
private static int parsedEipSerial;
private static JSONObject eipDefinition = null;
@@ -140,59 +126,136 @@ public final class EIP extends IntentService {
}
- @Override
- protected void onHandleIntent(Intent intent) {
- String action = intent.getAction();
- mReceiver = intent.getParcelableExtra(RECEIVER_TAG);
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ String action = intent.getAction();
+ mReceiver = intent.getParcelableExtra(RECEIVER_TAG);
- if ( action == ACTION_IS_EIP_RUNNING )
- this.isRunning();
- if ( action == ACTION_UPDATE_EIP_SERVICE )
- this.updateEIPService();
- else if ( action == ACTION_START_EIP )
- this.startEIP();
- else if ( action == ACTION_STOP_EIP )
- this.stopEIP();
- else if ( action == ACTION_CHECK_CERT_VALIDITY )
- this.checkCertValidity();
- else if ( action == ACTION_REBUILD_PROFILES ) {
- this.updateGateways();
- }
- }
+ if ( action == ACTION_START_EIP )
+ startEIP();
+ else if ( action == ACTION_STOP_EIP )
+ stopEIP();
+ else if ( action == ACTION_IS_EIP_RUNNING )
+ isRunning();
+ else if ( action == ACTION_UPDATE_EIP_SERVICE )
+ updateEIPService();
+ else if ( action == ACTION_CHECK_CERT_VALIDITY )
+ checkCertValidity();
+ else if ( action == ACTION_REBUILD_PROFILES )
+ updateGateways();
+ }
- /**
- * Checks the last stored status notified by ics-openvpn
- * Sends <code>Activity.RESULT_CANCELED</code> to the ResultReceiver that made the
- * request if it's not connected, <code>Activity.RESULT_OK</code> otherwise.
- */
+ /**
+ * Initiates an EIP connection by selecting a gateway and preparing and sending an
+ * Intent to {@link se.leap.openvpn.LaunchVPN}.
+ * It also sets up early routes.
+ */
+ private void startEIP() {
+ earlyRoutes();
+ activeGateway = selectGateway();
+
+ if(activeGateway != null && activeGateway.mVpnProfile != null) {
+ launchActiveGateway();
+ }
+ }
+
+ /**
+ * Early routes are routes that block traffic until a new
+ * VpnService is started properly.
+ */
+ private void earlyRoutes() {
+ Intent void_vpn_launcher = new Intent(context, VoidVpnLauncher.class);
+ void_vpn_launcher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(void_vpn_launcher);
+ }
+
+ /**
+ * Choose a gateway to connect to based on timezone from system locale data
+ *
+ * @return The gateway to connect to
+ */
+ private OVPNGateway selectGateway() {
+ String closest_location = closestGateway();
+ String chosen_host = chooseHost(closest_location);
+
+ return new OVPNGateway(chosen_host);
+ }
+
+ private String closestGateway() {
+ TreeMap<Integer, Set<String>> offsets = calculateOffsets();
+ return offsets.isEmpty() ? "" : offsets.firstEntry().getValue().iterator().next();
+ }
+
+ private TreeMap<Integer, Set<String>> calculateOffsets() {
+ TreeMap<Integer, Set<String>> offsets = new TreeMap<Integer, Set<String>>();
- private void isRunning() {
- Bundle resultData = new Bundle();
- resultData.putString(REQUEST_TAG, ACTION_IS_EIP_RUNNING);
- int resultCode = Activity.RESULT_CANCELED;
- boolean is_connected = isConnected();
-
- resultCode = (is_connected) ? Activity.RESULT_OK : Activity.RESULT_CANCELED;
-
- if (mReceiver != null){
- mReceiver.send(resultCode, resultData);
- }
-
- Log.d(TAG, "isRunning() = " + is_connected);
- }
+ int localOffset = Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 3600000;
- /**
- * Initiates an EIP connection by selecting a gateway and preparing and sending an
- * Intent to {@link se.leap.openvpn.LaunchVPN}
- */
- private void startEIP() {
- activeGateway = selectGateway();
+ JSONObject locations = availableLocations();
+ Iterator<String> locations_names = locations.keys();
+ while(locations_names.hasNext()) {
+ try {
+ String location_name = locations_names.next();
+ JSONObject location = locations.getJSONObject(location_name);
+
+ int dist = timezoneDistance(localOffset, location.optInt("timezone"));
+
+ Set<String> set = (offsets.get(dist) != null) ?
+ offsets.get(dist) : new HashSet<String>();
- if(activeGateway != null && activeGateway.mVpnProfile != null) {
- launchActiveGateway();
- }
+ set.add(location_name);
+ offsets.put(dist, set);
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ return offsets;
+ }
+
+ private JSONObject availableLocations() {
+ JSONObject locations = null;
+ try {
+ if(eipDefinition == null) updateEIPService();
+ locations = eipDefinition.getJSONObject("locations");
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
}
+ return locations;
+ }
+
+ private int timezoneDistance(int local_timezone, int remote_timezone) {
+ // Distance along the numberline of Prime Meridian centric, assumes UTC-11 through UTC+12
+ int dist = Math.abs(local_timezone - remote_timezone);
+
+ // Farther than 12 timezones and it's shorter around the "back"
+ if (dist > 12)
+ dist = 12 - (dist -12); // Well i'll be. Absolute values make equations do funny things.
+
+ return dist;
+ }
+
+ private String chooseHost(String location) {
+ String chosen_host = "";
+ try {
+ JSONArray gateways = eipDefinition.getJSONArray("gateways");
+ for (int i = 0; i < gateways.length(); i++) {
+ JSONObject gw = gateways.getJSONObject(i);
+ if ( gw.getString("location").equalsIgnoreCase(location) || location.isEmpty()){
+ chosen_host = eipDefinition.getJSONObject("locations").getJSONObject(gw.getString("location")).getString("name");
+ break;
+ }
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return chosen_host;
+ }
+
private void launchActiveGateway() {
Intent intent = new Intent(this,LaunchVPN.class);
intent.setAction(Intent.ACTION_MAIN);
@@ -202,30 +265,48 @@ public final class EIP extends IntentService {
intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true);
intent.putExtra(RECEIVER_TAG, mReceiver);
startActivity(intent);
- mPending = ACTION_START_EIP;
}
-
- /**
- * Disconnects the EIP connection gracefully through the bound service or forcefully
- * if there is no bound service. Sends a message to the requesting ResultReceiver.
- */
- private void stopEIP() {
- if(isConnected()) {
- Intent disconnect_vpn = new Intent(this, DisconnectVPN.class);
- disconnect_vpn.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(disconnect_vpn);
- mIsDisconnecting = true;
- lastConnectionStatusLevel = ConnectionStatus.UNKNOWN_LEVEL; // Wait for the decision of the user
- Log.d(TAG, "mIsDisconnecting = true");
- }
+
+ /**
+ * Disconnects the EIP connection gracefully through the bound service or forcefully
+ * if there is no bound service. Sends a message to the requesting ResultReceiver.
+ */
+ private void stopEIP() {
+ if(isConnected()) {
+ Intent disconnect_vpn = new Intent(this, DisconnectVPN.class);
+ disconnect_vpn.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(disconnect_vpn);
+ mIsDisconnecting = true;
+ lastConnectionStatusLevel = ConnectionStatus.UNKNOWN_LEVEL; // Wait for the decision of the user
+ Log.d(TAG, "mIsDisconnecting = true");
+ }
- if (mReceiver != null){
- Bundle resultData = new Bundle();
- resultData.putString(REQUEST_TAG, ACTION_STOP_EIP);
- mReceiver.send(Activity.RESULT_OK, resultData);
- }
+ tellToReceiver(ACTION_STOP_EIP, Activity.RESULT_OK);
+ }
+
+ private void tellToReceiver(String action, int resultCode) {
+ if (mReceiver != null){
+ Bundle resultData = new Bundle();
+ resultData.putString(REQUEST_TAG, action);
+ mReceiver.send(resultCode, resultData);
}
+ }
+
+ /**
+ * Checks the last stored status notified by ics-openvpn
+ * Sends <code>Activity.RESULT_CANCELED</code> to the ResultReceiver that made the
+ * request if it's not connected, <code>Activity.RESULT_OK</code> otherwise.
+ */
+
+ private void isRunning() {
+ int resultCode = Activity.RESULT_CANCELED;
+ boolean is_connected = isConnected();
+
+ resultCode = (is_connected) ? Activity.RESULT_OK : Activity.RESULT_CANCELED;
+ tellToReceiver(ACTION_IS_EIP_RUNNING, resultCode);
+ }
+
protected static boolean isConnected() {
return lastConnectionStatusLevel != null && lastConnectionStatusLevel.equals(ConnectionStatus.LEVEL_CONNECTED) && !mIsDisconnecting;
}
@@ -257,70 +338,6 @@ public final class EIP extends IntentService {
vpl.removeProfile(context, profiles[current_profile]);
}
}
- /**
- * Choose a gateway to connect to based on timezone from system locale data
- *
- * @return The gateway to connect to
- */
- private OVPNGateway selectGateway() {
- String closestLocation = closestGateway();
-
- JSONArray gateways = null;
- String chosenHost = null;
- try {
- gateways = eipDefinition.getJSONArray("gateways");
- for (int i = 0; i < gateways.length(); i++) {
- JSONObject gw = gateways.getJSONObject(i);
- if ( gw.getString("location").equalsIgnoreCase(closestLocation) || closestLocation.isEmpty()){
- chosenHost = eipDefinition.getJSONObject("locations").getJSONObject(gw.getString("location")).getString("name");
- break;
- }
- }
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- return new OVPNGateway(chosenHost);
- }
-
- private String closestGateway() {
- Calendar cal = Calendar.getInstance();
- int localOffset = cal.get(Calendar.ZONE_OFFSET) / 3600000;
- TreeMap<Integer, Set<String>> offsets = new TreeMap<Integer, Set<String>>();
- JSONObject locationsObjects = null;
- Iterator<String> locations = null;
- try {
- locationsObjects = eipDefinition.getJSONObject("locations");
- locations = locationsObjects.keys();
- } catch (JSONException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
-
- while (locations.hasNext()) {
- String locationName = locations.next();
- JSONObject location = null;
- try {
- location = locationsObjects.getJSONObject(locationName);
-
- // Distance along the numberline of Prime Meridian centric, assumes UTC-11 through UTC+12
- int dist = Math.abs(localOffset - location.optInt("timezone"));
- // Farther than 12 timezones and it's shorter around the "back"
- if (dist > 12)
- dist = 12 - (dist -12); // Well i'll be. Absolute values make equations do funny things.
-
- Set<String> set = offsets.get(dist);
- if (set == null) set = new HashSet<String>();
- set.add(locationName);
- offsets.put(dist, set);
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- return offsets.isEmpty() ? "" : offsets.firstEntry().getValue().iterator().next();
- }
/**
* Walk the list of gateways defined in eip-service.json and parse them into
diff --git a/app/src/main/java/se/leap/bitmaskclient/VoidVpnLauncher.java b/app/src/main/java/se/leap/bitmaskclient/VoidVpnLauncher.java
new file mode 100644
index 00000000..3b286fbf
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/VoidVpnLauncher.java
@@ -0,0 +1,37 @@
+package se.leap.bitmaskclient;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.VpnService;
+import android.os.Bundle;
+
+public class VoidVpnLauncher extends Activity {
+
+ private static final int VPN_USER_PERMISSION = 71;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setUp();
+ }
+
+ public void setUp() {
+ Intent blocking_intent = VpnService.prepare(getApplicationContext()); // stops the VPN connection created by another application.
+ if(blocking_intent != null)
+ startActivityForResult(blocking_intent, VPN_USER_PERMISSION);
+ else {
+ onActivityResult(VPN_USER_PERMISSION, RESULT_OK, null);
+ }
+ }
+
+ protected void onActivityResult(int requestCode, int resultCode, Intent data){
+ if(requestCode == VPN_USER_PERMISSION) {
+ if(resultCode == RESULT_OK) {
+ Intent void_vpn_service = new Intent(getApplicationContext(), VoidVpnService.class);
+ void_vpn_service.setAction(VoidVpnService.START_BLOCKING_VPN_PROFILE);
+ startService(void_vpn_service);
+ }
+ }
+ finish();
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/VoidVpnService.java b/app/src/main/java/se/leap/bitmaskclient/VoidVpnService.java
new file mode 100644
index 00000000..b7289c23
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/VoidVpnService.java
@@ -0,0 +1,42 @@
+package se.leap.bitmaskclient;
+
+import android.content.Intent;
+import android.net.VpnService;
+import android.util.Log;
+
+public class VoidVpnService extends VpnService {
+
+ static final String START_BLOCKING_VPN_PROFILE = "se.leap.bitmaskclient.START_BLOCKING_VPN_PROFILE";
+ static final String TAG = VoidVpnService.class.getSimpleName();
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ String action = intent.getAction();
+ if (action == START_BLOCKING_VPN_PROFILE) {
+ new Thread(new Runnable() {
+ public void run() {
+ blockConnections();
+ }
+ }).run();
+ }
+ return 0;
+ }
+
+ public void blockConnections() {
+ Builder builder = new Builder();
+ builder.setSession("Blocking until running");
+ builder.addAddress("10.42.0.8",16);
+ builder.addRoute("0.0.0.0", 1);
+ builder.addRoute("128.0.0.0", 1);
+ builder.addRoute("192.168.1.0", 24);
+ builder.addDnsServer("10.42.0.1");
+ builder.establish();
+ android.util.Log.d(TAG, "VoidVpnService set up");
+ try {
+ new java.net.Socket("sdf.org", 80);
+ Log.d(TAG, "VoidVpnService doesn's stop traffic");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}