summaryrefslogtreecommitdiff
path: root/app/src/main/java/se/leap/bitmaskclient/eip
diff options
context:
space:
mode:
authorParménides GV <parmegv@sdf.org>2014-11-12 01:30:09 +0100
committerParménides GV <parmegv@sdf.org>2014-11-26 12:16:14 +0100
commit261dc90595e583914161e5e9011f5f5dd4a9740c (patch)
tree4ebc89fdd6db41011a33f69a4dde83b5431d0897 /app/src/main/java/se/leap/bitmaskclient/eip
parent48cd0f2fa3094b5a6b7b07d6413d77bdbc9bbc20 (diff)
eip package, EIP constants to interface.
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient/eip')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/Constants.java50
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java486
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java37
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java37
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java145
5 files changed, 755 insertions, 0 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Constants.java b/app/src/main/java/se/leap/bitmaskclient/eip/Constants.java
new file mode 100644
index 00000000..719fff6d
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Constants.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2013 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+/**
+ *
+ * Constants for intent passing, shared preferences
+ *
+ * @author Parménides GV <parmegv@sdf.org>
+ *
+ */
+public interface Constants {
+
+ public final static String TAG = Constants.class.getSimpleName();
+
+ public final static String AUTHED_EIP = TAG + ".AUTHED_EIP";
+ public final static String ACTION_CHECK_CERT_VALIDITY = TAG + ".CHECK_CERT_VALIDITY";
+ public final static String ACTION_START_EIP = TAG + ".START_EIP";
+ public final static String ACTION_STOP_EIP = TAG + ".STOP_EIP";
+ public final static String ACTION_UPDATE_EIP_SERVICE = TAG + ".UPDATE_EIP_SERVICE";
+ public final static String ACTION_IS_EIP_RUNNING = TAG + ".IS_RUNNING";
+ public final static String ACTION_REBUILD_PROFILES = TAG + ".REBUILD_PROFILES";
+ public final static String EIP_NOTIFICATION = TAG + ".EIP_NOTIFICATION";
+ public final static String STATUS = TAG + ".STATUS";
+ public final static String DATE_FROM_CERTIFICATE = TAG + ".DATE_FROM_CERTIFICATE";
+ public final static String ALLOWED_ANON = TAG + ".ALLOW_ANONYMOUS";
+ public final static String ALLOWED_REGISTERED = TAG + ".ALLOW_REGISTRATION";
+ public final static String CERTIFICATE = TAG + ".CERTIFICATE";
+ public final static String PRIVATE_KEY = TAG + ".PRIVATE_KEY";
+ public final static String KEY = TAG + ".KEY";
+ public final static String PARSED_SERIAL = TAG + ".PARSED_SERIAL";
+ public final static String RECEIVER_TAG = TAG + ".RECEIVER_TAG";
+ public final static String REQUEST_TAG = TAG + ".REQUEST_TAG";
+ public final static String START_BLOCKING_VPN_PROFILE = TAG + ".START_BLOCKING_VPN_PROFILE";
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
new file mode 100644
index 00000000..b668ce64
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -0,0 +1,486 @@
+/**
+ * Copyright (c) 2013 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+import android.app.*;
+import android.content.*;
+import android.os.*;
+import android.util.Log;
+import java.io.*;
+import java.security.cert.*;
+import java.text.*;
+import java.util.*;
+import org.json.*;
+
+import de.blinkt.openvpn.*;
+import de.blinkt.openvpn.activities.*;
+import de.blinkt.openvpn.core.*;
+import se.leap.bitmaskclient.*;
+
+import static se.leap.bitmaskclient.eip.Constants.*;
+
+/**
+ * EIP is the abstract base class for interacting with and managing the Encrypted
+ * Internet Proxy connection. Connections are started, stopped, and queried through
+ * this IntentService.
+ * Contains logic for parsing eip-service.json from the provider, configuring and selecting
+ * gateways, and controlling {@link de.blinkt.openvpn.core.OpenVPNService} connections.
+ *
+ * @author Sean Leonard <meanderingcode@aetherislands.net>
+ * @author Parménides GV <parmegv@sdf.org>
+ */
+public final class EIP extends IntentService {
+
+ public final static String TAG = EIP.class.getSimpleName();
+
+ public final static String SERVICE_API_PATH = "config/eip-service.json";
+
+ private static SharedPreferences preferences;
+
+ private static Context context;
+ private static ResultReceiver mReceiver;
+ private static boolean mBound = false;
+
+ private static JSONObject eipDefinition = null;
+
+ private static OVPNGateway activeGateway = null;
+
+ public static VpnStatus.ConnectionStatus lastConnectionStatusLevel;
+ public static boolean mIsDisconnecting = false;
+ public static boolean mIsStarting = false;
+
+ public static SimpleDateFormat certificate_date_format = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
+
+ public EIP(){
+ super("LEAPEIP");
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ context = getApplicationContext();
+
+ preferences = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE);
+ }
+
+ @Override
+ public void onDestroy() {
+
+ mBound = false;
+
+ super.onDestroy();
+ }
+
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ String action = intent.getAction();
+ mReceiver = intent.getParcelableExtra(RECEIVER_TAG);
+
+ 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();
+ }
+
+ /**
+ * 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) {
+ mReceiver = EipServiceFragment.getReceiver();
+ 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>>();
+
+ int localOffset = Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 3600000;
+
+ 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>();
+
+ 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);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(LaunchVPN.EXTRA_KEY, activeGateway.mVpnProfile.getUUID().toString() );
+ intent.putExtra(LaunchVPN.EXTRA_NAME, activeGateway.mVpnProfile.getName() );
+ intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true);
+ intent.putExtra(RECEIVER_TAG, mReceiver);
+ startActivity(intent);
+ }
+
+ /**
+ * 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 = VpnStatus.ConnectionStatus.UNKNOWN_LEVEL; // Wait for the decision of the user
+ Log.d(TAG, "mIsDisconnecting = true");
+ }
+
+ 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);
+ }
+
+ public static boolean isConnected() {
+ return lastConnectionStatusLevel != null && lastConnectionStatusLevel.equals(VpnStatus.ConnectionStatus.LEVEL_CONNECTED) && !mIsDisconnecting;
+ }
+
+ /**
+ * Loads eip-service.json from SharedPreferences and calls {@link updateGateways()}
+ * to parse gateway definitions.
+ * TODO Implement API call to refresh eip-service.json from the provider
+ */
+ private void updateEIPService() {
+ try {
+ String eip_definition_string = preferences.getString(KEY, "");
+ if(eip_definition_string.isEmpty() == false) {
+ eipDefinition = new JSONObject(eip_definition_string);
+ }
+ deleteAllVpnProfiles();
+ updateGateways();
+ if(mReceiver != null) mReceiver.send(Activity.RESULT_OK, Bundle.EMPTY);
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private void deleteAllVpnProfiles() {
+ ProfileManager vpl = ProfileManager.getInstance(context);
+ Collection<VpnProfile> profiles = vpl.getProfiles();
+ profiles.removeAll(profiles);
+ }
+
+ /**
+ * Walk the list of gateways defined in eip-service.json and parse them into
+ * OVPNGateway objects.
+ * TODO Store the OVPNGateways (as Serializable) in SharedPreferences
+ */
+ private void updateGateways(){
+ JSONArray gatewaysDefined = null;
+ try {
+ if(eipDefinition == null) updateEIPService();
+ gatewaysDefined = eipDefinition.getJSONArray("gateways");
+ for ( int i=0 ; i < gatewaysDefined.length(); i++ ){
+ JSONObject gw = null;
+ gw = gatewaysDefined.getJSONObject(i);
+
+ if ( gw.getJSONObject("capabilities").getJSONArray("transport").toString().contains("openvpn") )
+ new OVPNGateway(gw);
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ preferences.edit().putInt(PARSED_SERIAL, eipDefinition.optInt(Provider.API_RETURN_SERIAL)).commit();
+ }
+
+ private void checkCertValidity() {
+ String certificate = preferences.getString(CERTIFICATE, "");
+ checkCertValidity(certificate);
+ }
+
+ private void checkCertValidity(String certificate_string) {
+ if(!certificate_string.isEmpty()) {
+ X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(certificate_string);
+
+ Calendar offset_date = calculateOffsetCertificateValidity(certificate);
+ Bundle result = new Bundle();
+ result.putString(REQUEST_TAG, ACTION_CHECK_CERT_VALIDITY);
+ try {
+ Log.d(TAG, "offset_date = " + offset_date.getTime().toString());
+ certificate.checkValidity(offset_date.getTime());
+ mReceiver.send(Activity.RESULT_OK, result);
+ Log.d(TAG, "Valid certificate");
+ } catch(CertificateExpiredException e) {
+ mReceiver.send(Activity.RESULT_CANCELED, result);
+ Log.d(TAG, "Updating certificate");
+ } catch(CertificateNotYetValidException e) {
+ mReceiver.send(Activity.RESULT_CANCELED, result);
+ }
+ }
+ }
+
+ private Calendar calculateOffsetCertificateValidity(X509Certificate certificate) {
+ String current_date = certificate_date_format.format(Calendar.getInstance().getTime()).toString();
+
+ String date_string = preferences.getString(DATE_FROM_CERTIFICATE, current_date);
+
+ Calendar offset_date = Calendar.getInstance();
+ try {
+ Date date = certificate_date_format.parse(date_string);
+ long difference = Math.abs(date.getTime() - certificate.getNotAfter().getTime())/2;
+ long current_date_millis = offset_date.getTimeInMillis();
+ offset_date.setTimeInMillis(current_date_millis + difference);
+ Log.d(TAG, "certificate not after = " + certificate.getNotAfter());
+ } catch(ParseException e) {
+ e.printStackTrace();
+ }
+
+ return offset_date;
+ }
+
+ /**
+ * OVPNGateway provides objects defining gateways and their options and metadata.
+ * Each instance contains a VpnProfile for OpenVPN specific data and member
+ * variables describing capabilities and location
+ *
+ * @author Sean Leonard <meanderingcode@aetherislands.net>
+ */
+ private class OVPNGateway {
+
+ private String TAG = "OVPNGateway";
+
+ private String mName;
+ private VpnProfile mVpnProfile;
+ private JSONObject mGateway;
+ private HashMap<String,Vector<Vector<String>>> options = new HashMap<String, Vector<Vector<String>>>();
+
+
+ /**
+ * Attempts to retrieve a VpnProfile by name and build an OVPNGateway around it.
+ * FIXME This needs to become a findGatewayByName() method
+ *
+ * @param name The hostname of the gateway to inflate
+ */
+ private OVPNGateway(String name){
+ mName = name;
+
+ this.loadVpnProfile();
+ }
+
+ private void loadVpnProfile() {
+ ProfileManager vpl = ProfileManager.getInstance(context);
+ try {
+ if ( mName == null )
+ mVpnProfile = vpl.getProfiles().iterator().next();
+ else
+ mVpnProfile = vpl.getProfileByName(mName);
+ } catch (NoSuchElementException e) {
+ updateEIPService();
+ this.loadVpnProfile(); // FIXME catch infinite loops
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Build a gateway object from a JSON OpenVPN gateway definition in eip-service.json
+ * and create a VpnProfile belonging to it.
+ *
+ * @param gateway The JSON OpenVPN gateway definition to parse
+ */
+ protected OVPNGateway(JSONObject gateway){
+
+ mGateway = gateway;
+
+ // Currently deletes VpnProfile for host, if there already is one, and builds new
+ ProfileManager vpl = ProfileManager.getInstance(context);
+ Collection<VpnProfile> profiles = vpl.getProfiles();
+ for (Iterator<VpnProfile> it = profiles.iterator(); it.hasNext(); ){
+ VpnProfile p = it.next();
+
+ if ( p.mName.equalsIgnoreCase( mName ) ) {
+ it.remove();
+ vpl.removeProfile(context, p);
+ }
+ }
+
+ this.createVPNProfile();
+
+ vpl.addProfile(mVpnProfile);
+ vpl.saveProfile(context, mVpnProfile);
+ vpl.saveProfileList(context);
+ }
+
+ /**
+ * Create and attach the VpnProfile to our gateway object
+ */
+ protected void createVPNProfile(){
+ try {
+ ConfigParser cp = new ConfigParser();
+
+ JSONObject openvpn_configuration = eipDefinition.getJSONObject("openvpn_configuration");
+ VpnConfigGenerator vpn_configuration_generator = new VpnConfigGenerator(preferences, openvpn_configuration, mGateway);
+ String configuration = vpn_configuration_generator.generate();
+
+ cp.parseConfig(new StringReader(configuration));
+ mVpnProfile = cp.convertProfile();
+ mVpnProfile.mName = mName = locationAsName();
+ Log.v(TAG,"Created VPNProfile");
+
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (ConfigParser.ConfigParseError e) {
+ // FIXME We didn't get a VpnProfile! Error handling! and log level
+ Log.v(TAG,"Error creating VPNProfile");
+ e.printStackTrace();
+ } catch (IOException e) {
+ // FIXME We didn't get a VpnProfile! Error handling! and log level
+ Log.v(TAG,"Error creating VPNProfile");
+ e.printStackTrace();
+ }
+ }
+
+
+ public String locationAsName() {
+ try {
+ return eipDefinition.getJSONObject("locations").getJSONObject(mGateway.getString("location")).getString("name");
+ } catch (JSONException e) {
+ Log.v(TAG,"Couldn't read gateway name for profile creation! Returning original name = " + mName);
+ e.printStackTrace();
+ return (mName != null) ? mName : "";
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java
new file mode 100644
index 00000000..9814c167
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java
@@ -0,0 +1,37 @@
+package se.leap.bitmaskclient.eip;
+
+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(Constants.START_BLOCKING_VPN_PROFILE);
+ startService(void_vpn_service);
+ }
+ }
+ finish();
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
new file mode 100644
index 00000000..224e3bd4
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
@@ -0,0 +1,37 @@
+package se.leap.bitmaskclient.eip;
+
+import android.content.Intent;
+import android.os.Process;
+import android.net.VpnService;
+import android.util.Log;
+
+import static se.leap.bitmaskclient.eip.Constants.*;
+
+public class VoidVpnService extends VpnService {
+
+ static final String TAG = VoidVpnService.class.getSimpleName();
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ String action = intent.getAction();
+ if (action == Constants.START_BLOCKING_VPN_PROFILE) {
+ new Thread(new Runnable() {
+ public void run() {
+ 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("192.168.1.0", 24);
+ builder.addDnsServer("10.42.0.1");
+ try {
+ builder.establish();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ android.util.Log.d(TAG, "VoidVpnService set up");
+ }
+ }).run();
+ }
+ return 0;
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
new file mode 100644
index 00000000..8e36f53c
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
@@ -0,0 +1,145 @@
+/**
+ * Copyright (c) 2013 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+import android.content.SharedPreferences;
+import android.util.Log;
+import java.util.Iterator;
+import java.util.Vector;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.json.JSONException;
+
+import se.leap.bitmaskclient.*;
+
+public class VpnConfigGenerator {
+
+ private JSONObject general_configuration;
+ private JSONObject gateway;
+
+ private static SharedPreferences preferences;
+ public final static String TAG = VpnConfigGenerator.class.getSimpleName();
+ private final String new_line = System.getProperty("line.separator"); // Platform new line
+
+ public VpnConfigGenerator(SharedPreferences preferences, JSONObject general_configuration, JSONObject gateway) {
+ this.general_configuration = general_configuration;
+ this.gateway = gateway;
+ this.preferences = preferences;
+ }
+
+ public String generate() {
+ return
+ generalConfiguration()
+ + new_line
+ + gatewayConfiguration()
+ + new_line
+ + secretsConfiguration()
+ + new_line
+ + androidCustomizations();
+ }
+
+ private String generalConfiguration() {
+ String common_options = "";
+ try {
+ Iterator keys = general_configuration.keys();
+ Vector<Vector<String>> value = new Vector<Vector<String>>();
+ while ( keys.hasNext() ){
+ String key = keys.next().toString();
+
+ common_options += key + " ";
+ for ( String word : general_configuration.getString(key).split(" ") )
+ common_options += word + " ";
+ common_options += new_line;
+
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ common_options += "client";
+
+ return common_options;
+ }
+
+ private String gatewayConfiguration() {
+ String remotes = "";
+
+ String remote = "ip_address";
+ String remote_openvpn_keyword = "remote";
+ String ports = "ports";
+ String protos = "protocols";
+ String capabilities = "capabilities";
+ String udp = "udp";
+
+ try {
+ JSONArray protocolsJSON = gateway.getJSONObject(capabilities).getJSONArray(protos);
+ for ( int i=0; i<protocolsJSON.length(); i++ ) {
+ String remote_line = remote_openvpn_keyword;
+ remote_line += " " + gateway.getString(remote);
+ remote_line += " " + gateway.getJSONObject(capabilities).getJSONArray(ports).optString(0);
+ remote_line += " " + protocolsJSON.optString(i);
+ if(remote_line.endsWith(udp))
+ remotes = remotes.replaceFirst(remote_openvpn_keyword, remote_line + new_line + remote_openvpn_keyword);
+ else
+ remotes += remote_line;
+ remotes += new_line;
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ Log.d(TAG, "remotes = " + remotes);
+ return remotes;
+ }
+
+ private String secretsConfiguration() {
+
+ String ca =
+ "<ca>"
+ + new_line
+ + preferences.getString(Provider.CA_CERT, "")
+ + new_line
+ + "</ca>";
+
+ String key =
+ "<key>"
+ + new_line
+ + preferences.getString(Constants.PRIVATE_KEY, "")
+ + new_line
+ + "</key>";
+
+ String openvpn_cert =
+ "<cert>"
+ + new_line
+ + preferences.getString(Constants.CERTIFICATE, "")
+ + new_line
+ + "</cert>";
+
+ return ca + new_line + key + new_line + openvpn_cert;
+ }
+
+ private String androidCustomizations() {
+ return
+ "remote-cert-tls server"
+ + new_line
+ + "persist-tun"
+ + new_line
+ + "auth-retry nointeract";
+ }
+}