diff options
6 files changed, 324 insertions, 138 deletions
@@ -75,7 +75,6 @@ gradle.properties* build .gradle jniLibs -G* *.dot /ics-openvpn/ /TAGS diff --git a/app/src/main/java/de/blinkt/openvpn/core/GetRestrictionReceiver.java b/app/src/main/java/de/blinkt/openvpn/core/GetRestrictionReceiver.java new file mode 100644 index 00000000..5b1dda58 --- /dev/null +++ b/app/src/main/java/de/blinkt/openvpn/core/GetRestrictionReceiver.java @@ -0,0 +1,47 @@ +package de.blinkt.openvpn.core; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.RestrictionEntry; +import android.os.Build; +import android.os.Bundle; + +import java.util.ArrayList; + +import se.leap.bitmaskclient.R; + +/** + * Created by arne on 25.07.13. + */ +@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) +public class GetRestrictionReceiver extends BroadcastReceiver { + @Override + public void onReceive(final Context context, Intent intent) { + final PendingResult result = goAsync(); + + new Thread() { + @Override + public void run() { + final Bundle extras = new Bundle(); + + ArrayList<RestrictionEntry> restrictionEntries = initRestrictions(context); + + extras.putParcelableArrayList(Intent.EXTRA_RESTRICTIONS_LIST, restrictionEntries); + result.setResult(Activity.RESULT_OK,null,extras); + result.finish(); + } + }.run(); + } + + private ArrayList<RestrictionEntry> initRestrictions(Context context) { + ArrayList<RestrictionEntry> restrictions = new ArrayList<RestrictionEntry>(); + RestrictionEntry allowChanges = new RestrictionEntry("allow_changes",false); + allowChanges.setTitle(context.getString(R.string.allow_vpn_changes)); + restrictions.add(allowChanges); + + return restrictions; + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java index 9f81f263..876ad172 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -58,7 +58,9 @@ public final class EIP extends IntentService { private static int parsedEipSerial; private static JSONObject eip_definition = null; - private static OVPNGateway activeGateway = null; + private static Gateway activeGateway = null; + private static List<Gateway> gateways = new ArrayList<Gateway>(); + ProfileManager profile_manager; public static VpnStatus.ConnectionStatus lastConnectionStatusLevel; public static boolean mIsDisconnecting = false; @@ -73,6 +75,8 @@ public final class EIP extends IntentService { super.onCreate(); context = getApplicationContext(); + profile_manager = ProfileManager.getInstance(context); + preferences = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE); refreshEipDefinition(); } @@ -110,11 +114,10 @@ public final class EIP extends IntentService { */ private void startEIP() { earlyRoutes(); - GatewaySelector gateway_selector = new GatewaySelector(eip_definition); - String selected_gateway = gateway_selector.select(); + GatewaySelector gateway_selector = new GatewaySelector(gateways); - activeGateway = new OVPNGateway(selected_gateway); - if(activeGateway != null && activeGateway.mVpnProfile != null) { + activeGateway = gateway_selector.select(); + if(activeGateway != null && activeGateway.getProfile() != null) { mReceiver = EipServiceFragment.getReceiver(); launchActiveGateway(); } @@ -134,8 +137,8 @@ public final class EIP extends IntentService { 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_KEY, activeGateway.getProfile().getUUID().toString() ); + intent.putExtra(LaunchVPN.EXTRA_NAME, activeGateway.getProfile().getName() ); intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true); intent.putExtra(RECEIVER_TAG, mReceiver); startActivity(intent); @@ -210,26 +213,23 @@ public final class EIP extends IntentService { } private void deleteAllVpnProfiles() { - ProfileManager vpl = ProfileManager.getInstance(context); - Collection<VpnProfile> profiles = vpl.getProfiles(); + Collection<VpnProfile> profiles = profile_manager.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 - */ + /** + * Walk the list of gateways defined in eip-service.json and parse them into + * Gateway objects. + * TODO Store the Gateways (as Serializable) in SharedPreferences + */ private void updateGateways(){ - JSONArray gatewaysDefined = null; try { - gatewaysDefined = eip_definition.getJSONArray("gateways"); + JSONArray gatewaysDefined = eip_definition.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); + JSONObject gw = gatewaysDefined.getJSONObject(i); + if(isOpenVpnGateway(gw)) { + addGateway(new Gateway(eip_definition, context, gw)); + } } } catch (JSONException e) { // TODO Auto-generated catch block @@ -238,6 +238,20 @@ public final class EIP extends IntentService { preferences.edit().putInt(PARSED_SERIAL, eip_definition.optInt(Provider.API_RETURN_SERIAL)).commit(); } + private boolean isOpenVpnGateway(JSONObject gateway) { + try { + String transport = gateway.getJSONObject("capabilities").getJSONArray("transport").toString(); + return transport.contains("openvpn"); + } catch (JSONException e) { + return false; + } + } + + private void addGateway(Gateway gateway) { + profile_manager.addProfile(gateway.getProfile()); + gateways.add(gateway); + } + private void checkCertValidity() { VpnCertificateValidator validator = new VpnCertificateValidator(); int resultCode = validator.isValid(preferences.getString(CERTIFICATE, "")) ? @@ -245,120 +259,4 @@ public final class EIP extends IntentService { Activity.RESULT_CANCELED; tellToReceiver(ACTION_CHECK_CERT_VALIDITY, resultCode); } - - /** - * 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 = eip_definition.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 eip_definition.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/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java new file mode 100644 index 00000000..6aa66ac0 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java @@ -0,0 +1,151 @@ +/** + * 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.Activity; +import android.content.*; +import android.util.Log; +import java.io.*; +import java.util.*; +import org.json.*; + +import de.blinkt.openvpn.*; +import de.blinkt.openvpn.activities.*; +import de.blinkt.openvpn.core.*; +import se.leap.bitmaskclient.*; + +/** + * Gateway provides objects defining gateways and their metadata. + * Each instance contains a VpnProfile for OpenVPN specific data and member + * variables describing capabilities and location (name) + * + * @author Sean Leonard <meanderingcode@aetherislands.net> + * @author Parménides GV <parmegv@sdf.org> + */ +public class Gateway { + + private String TAG = Gateway.class.getSimpleName(); + + private String mName; + private int timezone; + private JSONObject general_configuration; + private Context context; + private VpnProfile mVpnProfile; + private JSONObject mGateway; + + /** + * 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 Gateway(JSONObject eip_definition, Context context, JSONObject gateway){ + + mGateway = gateway; + + this.context = context; + general_configuration = getGeneralConfiguration(eip_definition); + timezone = getTimezone(eip_definition); + mName = locationAsName(eip_definition); + + // 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); + } + } + + mVpnProfile = createVPNProfile(); + mVpnProfile.mName = mName; + + vpl.addProfile(mVpnProfile); + vpl.saveProfile(context, mVpnProfile); + vpl.saveProfileList(context); + } + + private JSONObject getGeneralConfiguration(JSONObject eip_definition) { + try { + return eip_definition.getJSONObject("openvpn_configuration"); + } catch (JSONException e) { + return new JSONObject(); + } + } + + private int getTimezone(JSONObject eip_definition) { + JSONObject location = getLocationInfo(eip_definition); + return location.optInt("timezone"); + } + + private String locationAsName(JSONObject eip_definition) { + JSONObject location = getLocationInfo(eip_definition); + return location.optString("name"); + } + + private JSONObject getLocationInfo(JSONObject eip_definition) { + try { + JSONObject locations = eip_definition.getJSONObject("locations"); + JSONObject location = locations.getJSONObject(mGateway.getString("location")); + + return location; + } catch (JSONException e) { + return new JSONObject(); + } + } + + /** + * Create and attach the VpnProfile to our gateway object + */ + private VpnProfile createVPNProfile(){ + try { + ConfigParser cp = new ConfigParser(); + + SharedPreferences preferences = context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE); + VpnConfigGenerator vpn_configuration_generator = new VpnConfigGenerator(preferences, general_configuration, mGateway); + String configuration = vpn_configuration_generator.generate(); + + cp.parseConfig(new StringReader(configuration)); + return cp.convertProfile(); + } catch (ConfigParser.ConfigParseError e) { + // FIXME We didn't get a VpnProfile! Error handling! and log level + Log.v(TAG,"Error creating VPNProfile"); + e.printStackTrace(); + return null; + } catch (IOException e) { + // FIXME We didn't get a VpnProfile! Error handling! and log level + Log.v(TAG,"Error creating VPNProfile"); + e.printStackTrace(); + return null; + } + } + + public String getName() { + return mName; + } + + public VpnProfile getProfile() { + return mVpnProfile; + } + + public int getTimezone() { + return timezone; + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java new file mode 100644 index 00000000..36b5fb41 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java @@ -0,0 +1,44 @@ +package se.leap.bitmaskclient.eip; + +import java.security.cert.*; +import java.util.*; +import org.json.*; + +public class GatewaySelector { + List<Gateway> gateways; + + public GatewaySelector(List<Gateway> gateways) { + this.gateways = gateways; + } + + public Gateway select() { + return closestGateway(); + } + + private Gateway closestGateway() { + TreeMap<Integer, Set<Gateway>> offsets = calculateOffsets(); + return offsets.isEmpty() ? null : offsets.firstEntry().getValue().iterator().next(); + } + + private TreeMap<Integer, Set<Gateway>> calculateOffsets() { + TreeMap<Integer, Set<Gateway>> offsets = new TreeMap<Integer, Set<Gateway>>(); + int localOffset = Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 3600000; + for(Gateway gateway : gateways) { + int dist = timezoneDistance(localOffset, gateway.getTimezone()); + Set<Gateway> set = (offsets.get(dist) != null) ? + offsets.get(dist) : new HashSet<Gateway>(); + set.add(gateway); + offsets.put(dist, set); + } + return offsets; + } + + 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; + } +} diff --git a/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/GetRestrictionReceiver.java b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/GetRestrictionReceiver.java new file mode 100644 index 00000000..5b1dda58 --- /dev/null +++ b/ics-openvpn-stripped/main/src/main/java/de/blinkt/openvpn/core/GetRestrictionReceiver.java @@ -0,0 +1,47 @@ +package de.blinkt.openvpn.core; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.RestrictionEntry; +import android.os.Build; +import android.os.Bundle; + +import java.util.ArrayList; + +import se.leap.bitmaskclient.R; + +/** + * Created by arne on 25.07.13. + */ +@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) +public class GetRestrictionReceiver extends BroadcastReceiver { + @Override + public void onReceive(final Context context, Intent intent) { + final PendingResult result = goAsync(); + + new Thread() { + @Override + public void run() { + final Bundle extras = new Bundle(); + + ArrayList<RestrictionEntry> restrictionEntries = initRestrictions(context); + + extras.putParcelableArrayList(Intent.EXTRA_RESTRICTIONS_LIST, restrictionEntries); + result.setResult(Activity.RESULT_OK,null,extras); + result.finish(); + } + }.run(); + } + + private ArrayList<RestrictionEntry> initRestrictions(Context context) { + ArrayList<RestrictionEntry> restrictions = new ArrayList<RestrictionEntry>(); + RestrictionEntry allowChanges = new RestrictionEntry("allow_changes",false); + allowChanges.setTitle(context.getString(R.string.allow_vpn_changes)); + restrictions.add(allowChanges); + + return restrictions; + } +} |