From 9a9823f7e5bf0e46e360ba327ac6514ecd4bb320 Mon Sep 17 00:00:00 2001 From: Sean Leonard Date: Sun, 9 Jun 2013 02:08:52 -0600 Subject: Introducing the EIP class! Parses eip-service.json for OpenVPN gateways and builds VpnProfiles out of them --- src/se/leap/leapclient/Dashboard.java | 2 + src/se/leap/leapclient/EIP.java | 294 ++++++++++++++++++++++++++++++++ src/se/leap/openvpn/ConfigParser.java | 6 +- src/se/leap/openvpn/OpenVpnService.java | 1 - 4 files changed, 301 insertions(+), 2 deletions(-) create mode 100644 src/se/leap/leapclient/EIP.java diff --git a/src/se/leap/leapclient/Dashboard.java b/src/se/leap/leapclient/Dashboard.java index dd8ee335..19c635a3 100644 --- a/src/se/leap/leapclient/Dashboard.java +++ b/src/se/leap/leapclient/Dashboard.java @@ -69,6 +69,8 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf if ( resultCode == RESULT_OK ){ // Configuration done, get our preferences again preferences = getSharedPreferences(ConfigHelper.PREFERENCES_KEY,MODE_PRIVATE); + // Update eip-service local parsing + startService( new Intent(EIP.ACTION_UPDATE_EIP_SERVICE) ); buildDashboard(); } else { diff --git a/src/se/leap/leapclient/EIP.java b/src/se/leap/leapclient/EIP.java new file mode 100644 index 00000000..f86dd083 --- /dev/null +++ b/src/se/leap/leapclient/EIP.java @@ -0,0 +1,294 @@ +package se.leap.leapclient; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Vector; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import se.leap.openvpn.ConfigParser; +import se.leap.openvpn.ConfigParser.ConfigParseError; +import se.leap.openvpn.ProfileManager; +import se.leap.openvpn.VpnProfile; +import android.app.IntentService; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +/** + * @author Sean Leonard + * + */ +public final class EIP extends IntentService { + + public final static String ACTION_UPDATE_EIP_SERVICE = "se.leap.leapclient.UPDATE_EIP_SERVICE"; + + private static Context context; + + // Represents our Provider's eip-service.json + private static JSONObject eipDefinition = null; + + + public EIP(){ + super("LEAPEIP"); + } + + @Override + public void onCreate() { + super.onCreate(); + + context = getApplicationContext(); + + // Inflate our eip-service.json data + try { + eipDefinition = ConfigHelper.getJsonFromSharedPref(ConfigHelper.EIP_SERVICE_KEY); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Override + protected void onHandleIntent(Intent intent) { + // Get our action from the Intent + String action = intent.getAction(); + + if ( action == ACTION_UPDATE_EIP_SERVICE ) + this.updateEIPService(); + } + + private void updateEIPService() { + // TODO this will also fetch new eip-service.json + try { + eipDefinition = ConfigHelper.getJsonFromSharedPref(ConfigHelper.EIP_SERVICE_KEY); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + updateGateways(); + } + + private void updateGateways(){ + JSONArray gatewaysDefined = null; + + // Get our list of gateways + try { + gatewaysDefined = eipDefinition.getJSONArray("gateways"); + } catch (JSONException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + + // Walk the list of gateways and inflate them to VPNProfiles + for ( int i=0 ; i < gatewaysDefined.length(); i++ ){ + + JSONObject gw = null; + + try { + gw = gatewaysDefined.getJSONObject(i); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + if ( gw.getJSONObject("capabilities").getJSONArray("transport").toString().contains("openvpn") ){ + // We have an openvpn gateway! + // Now build VPNProfiles and save their UUIDs + // TODO create multiple profiles for each gateway to allow trying e.g. different ports when connections don't complete + new OVPNGateway(gw); + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + private class OVPNGateway { + + // Log tag + private String TAG = "OVPNGateway"; + + // The actual VPN Profile object + private VpnProfile mVpnProfile; + // Our gateway definition from eip-service.json + private JSONObject gateway; + // This holds our OpenVPN options for creating the VPNProfile + // Options get put here in the form that se.leap.openvpn.ConfigParser wants TODO will be gone w/ rewrite + private HashMap>> options = new HashMap>>(); + + + // Constructor to create a gateway by definition + protected OVPNGateway(JSONObject gw){ + // TODO We're going to build 1 profile per gateway, but eventually several + + gateway = gw; + + // Delete VpnProfile for host, if there already is one + // FIXME There is a better way to check settings and update them, instead of destroy/rebuild + // Also, this allows one gateway per hostname entry, so that had better be true from the server! + // TODO Will we define multiple gateways per host, for variable options? or change how .openvpn.VpnProfile works? + ProfileManager vpl = ProfileManager.getInstance(context); + Collection profiles = vpl.getProfiles(); + for (VpnProfile p : profiles){ + try { + if ( p.mName.contains( gateway.getString("host") ) ) + vpl.removeProfile(context, p); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // Create options HashMap for se.leap.openvpn.ConfigParser + this.parseOptions(); + // Now create the VPNProfile + this.createVPNProfile(); + // Now let's save it in the .openvpn package way + + setUniqueProfileName(vpl); + vpl.addProfile(mVpnProfile); + vpl.saveProfile(context, mVpnProfile); + vpl.saveProfileList(context); + } + + private void setUniqueProfileName(ProfileManager vpl) { + int i=0; + + String newname; + try { + newname = gateway.getString("host"); + while(vpl.getProfileByName(newname)!=null) { + i++; + if(i==1) + newname = getString(R.string.converted_profile); + else + newname = getString(R.string.converted_profile_i,i); + } + + mVpnProfile.mName=newname; + } catch (JSONException e) { + // TODO Auto-generated catch block + Log.v(TAG,"Couldn't read gateway name for profile creation!"); + e.printStackTrace(); + } + } + + // FIXME this whole thing will get rewritten when we modify ConfigParser + // in fact, don't even bother looking, except to debug + private void parseOptions(){ + // TODO we will want to rewrite se.leap.openvpn.ConfigParser soon to be targeted at our use + + // FIXME move these to a common API (& version) definition place, like ProviderAPI or ConfigHelper + String common_options = "openvpn_configuration"; + String remote = "ip_address"; + String ports = "ports"; + String protos = "protocols"; + String capabilities = "capabilities"; + + // FIXME Our gateway definition has keys that are not OpenVPN options... + // We need a hard spec for the eip-service.json and better handling in this class + // Then we can stop dumping all the "capabilities" key:values into our options for parsing + + // Put our common options in + // FIXME quite ugly. We don't need the nested vectors, as we're not byte-reading file input, but we haven't rewritten the parser, yet + + Vector arg = new Vector(); + Vector> args = new Vector>(); + + try { + JSONObject def = (JSONObject) eipDefinition.get(common_options); + Iterator keys = def.keys(); + Vector> value = new Vector>(); + while ( keys.hasNext() ){ + String key = keys.next().toString(); + + arg.add(key); + for ( String word : def.getString(key).split(" ") ) + arg.add(word); + value.add( (Vector) arg.clone() ); + options.put(key, (Vector>) value.clone()); + + value.clear(); + arg.clear(); + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + // Now our gateway-specific options + // to hold 'em the way they're wanted for parsing + + // remote:ip_address + try { + arg.add("remote"); + arg.add(gateway.getString(remote)); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + args.add((Vector) arg.clone()); + options.put("remote", (Vector>) args.clone() ); + arg.clear(); + args.clear(); + + // proto:udp||tcp + JSONArray protocolsJSON = null; + arg.add("proto"); + try { + protocolsJSON = gateway.getJSONObject(capabilities).getJSONArray(protos); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Vector protocols = new Vector(); + for ( int i=0; i) arg.clone()); + options.put("proto", (Vector>) args.clone()); + arg.clear(); + args.clear(); + + + // Now ports...picking one 'cause i say so'... TODO we should have multiple profiles?... + String port = null; + arg.add("port"); + try { + port = gateway.getJSONObject(capabilities).getJSONArray(ports).optString(0); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + arg.add(port); + args.add((Vector) arg.clone()); + options.put("port", (Vector>) args.clone()); + args.clear(); + arg.clear(); + } + + protected void createVPNProfile(){ + // TODO take data from eip-service.json for openvpn gateway definitions and create VPNProfile for each + try { + ConfigParser cp = new ConfigParser(); + cp.setDefinition(options); + VpnProfile vp = cp.convertProfile(); + mVpnProfile = vp; + Log.v(TAG,"Created VPNProfile"); + } catch (ConfigParseError e) { + // FIXME Being here is bad because we didn't get a VpnProfile! + Log.v(TAG,"Error createing VPNProfile"); + e.printStackTrace(); + } + } + } + +} diff --git a/src/se/leap/openvpn/ConfigParser.java b/src/se/leap/openvpn/ConfigParser.java index f57bbae9..3d369fa6 100644 --- a/src/se/leap/openvpn/ConfigParser.java +++ b/src/se/leap/openvpn/ConfigParser.java @@ -47,6 +47,9 @@ public class ConfigParser { options.get(optionname).add(args); } } + public void setDefinition(HashMap>> args) { + options = args; + } private void checkinlinefile(Vector args, BufferedReader br) throws IOException, ConfigParseError { String arg0 = args.get(0); @@ -247,7 +250,8 @@ public class ConfigParser { // Pull, client, tls-client np.clearDefaults(); - if(options.containsKey("client") || options.containsKey("pull")) { + // XXX we are always client + if(/*options.containsKey("client") || options.containsKey("pull")*/ true) { np.mUsePull=true; options.remove("pull"); options.remove("client"); diff --git a/src/se/leap/openvpn/OpenVpnService.java b/src/se/leap/openvpn/OpenVpnService.java index c745ee3b..42c1de8a 100644 --- a/src/se/leap/openvpn/OpenVpnService.java +++ b/src/se/leap/openvpn/OpenVpnService.java @@ -35,7 +35,6 @@ import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.net.VpnService; import android.os.Binder; -import android.os.Handler; import android.os.Handler.Callback; import android.os.Build; import android.os.IBinder; -- cgit v1.2.3