From 8f7146a89fba31bcb9a204415a38e796cfa7d403 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 14 Jun 2019 18:18:18 +0200 Subject: * refactor vpn profile generation * fix lzo-comp flag parsing in ConfigParser --- .../java/se/leap/bitmaskclient/eip/Gateway.java | 2 +- .../se/leap/bitmaskclient/eip/GatewaysManager.java | 10 +- .../leap/bitmaskclient/eip/VpnConfigGenerator.java | 145 +++++++++++---------- .../pluggableTransports/Dispatcher.java | 35 +++-- .../pluggableTransports/DispatcherOptions.java | 18 +++ 5 files changed, 123 insertions(+), 87 deletions(-) create mode 100644 app/src/main/java/se/leap/bitmaskclient/pluggableTransports/DispatcherOptions.java (limited to 'app/src/main/java/se') diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java index b1554af0..50fe74b7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java @@ -114,7 +114,7 @@ public class Gateway { try { VpnConfigGenerator vpnConfigurationGenerator = new VpnConfigGenerator(generalConfiguration, secrets, gateway, apiVersion); return vpnConfigurationGenerator.generateVpnProfile(); - } catch (ConfigParser.ConfigParseError | IOException | CloneNotSupportedException | JSONException e) { + } catch (ConfigParser.ConfigParseError | IOException | JSONException e) { // FIXME We didn't get a VpnProfile! Error handling! and log level e.printStackTrace(); return null; diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java index c650938c..cd3ec1c6 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -99,12 +99,10 @@ public class GatewaysManager { int apiVersion = eipDefinition.getInt(VERSION); for (int i = 0; i < gatewaysDefined.length(); i++) { JSONObject gw = gatewaysDefined.getJSONObject(i); - if (isOpenVpnGateway(gw, apiVersion)) { - JSONObject secrets = secretsConfiguration(); - Gateway aux = new Gateway(eipDefinition, secrets, gw); - if (!gateways.contains(aux)) { - addGateway(aux); - } + JSONObject secrets = secretsConfiguration(); + Gateway aux = new Gateway(eipDefinition, secrets, gw); + if (!gateways.contains(aux)) { + addGateway(aux); } } } catch (JSONException e) { diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java index 7f09d21e..a131bdd8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java @@ -27,9 +27,11 @@ import java.util.Iterator; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ConfigParser; import de.blinkt.openvpn.core.connection.Connection; -import de.blinkt.openvpn.core.connection.Obfs4Connection; import se.leap.bitmaskclient.Provider; +import se.leap.bitmaskclient.pluggableTransports.DispatcherOptions; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; +import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN; import static se.leap.bitmaskclient.Constants.CAPABILITIES; import static se.leap.bitmaskclient.Constants.IP_ADDRESS; import static se.leap.bitmaskclient.Constants.OPTIONS; @@ -40,9 +42,10 @@ import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.Constants.REMOTE; import static se.leap.bitmaskclient.Constants.TRANSPORT; import static se.leap.bitmaskclient.Constants.TYPE; +import static se.leap.bitmaskclient.pluggableTransports.Dispatcher.DISPATCHER_IP; +import static se.leap.bitmaskclient.pluggableTransports.Dispatcher.DISPATCHER_PORT; public class VpnConfigGenerator { - private JSONObject generalConfiguration; private JSONObject gateway; private JSONObject secrets; @@ -66,18 +69,14 @@ public class VpnConfigGenerator { public void checkCapabilities() { try { - switch (apiVersion) { - case 2: - JSONArray supportedTransports = gateway.getJSONObject(CAPABILITIES).getJSONArray(TRANSPORT); - for (int i = 0; i < supportedTransports.length(); i++) { - JSONObject transport = supportedTransports.getJSONObject(i); - if (transport.getString(TYPE).equals("obfs4")) { - obfs4Transport = transport; - } + if (apiVersion == 2) { + JSONArray supportedTransports = gateway.getJSONObject(CAPABILITIES).getJSONArray(TRANSPORT); + for (int i = 0; i < supportedTransports.length(); i++) { + JSONObject transport = supportedTransports.getJSONObject(i); + if (transport.getString(TYPE).equals(OBFS4.toString())) { + obfs4Transport = transport; } - break; - default: - break; + } } } catch (JSONException e) { @@ -88,54 +87,45 @@ public class VpnConfigGenerator { public VpnProfile generateVpnProfile() throws IllegalStateException, IOException, ConfigParser.ConfigParseError, - CloneNotSupportedException, - JSONException, - NumberFormatException { + NumberFormatException, JSONException { - VpnProfile profile = createOvpnProfile(); if (supportsObfs4()) { - addPluggableTransportConnections(profile); + return createProfile(OBFS4); } - return profile; + + return createProfile(OPENVPN); } private boolean supportsObfs4(){ return obfs4Transport != null; } - private void addPluggableTransportConnections(VpnProfile profile) throws JSONException, CloneNotSupportedException { - JSONArray ports = obfs4Transport.getJSONArray(PORTS); - Connection[] updatedConnections = new Connection[profile.mConnections.length + ports.length()]; - - for (int i = 0; i < ports.length(); i++) { - String port = ports.getString(i); - Obfs4Connection obfs4Connection = new Obfs4Connection(); - obfs4Connection.setObfs4RemoteProxyName(gateway.getString(IP_ADDRESS)); - obfs4Connection.setObfs4RemoteProxyPort(port); - obfs4Connection.setTransportOptions(obfs4Transport.optJSONObject(OPTIONS)); - updatedConnections[i] = obfs4Connection; - } - int k = 0; - for (int i = ports.length(); i < updatedConnections.length; i++, k++) { - updatedConnections[i] = profile.mConnections[k].clone(); - } - profile.mConnections = updatedConnections; - } - - private String getConfigurationString() { + private String getConfigurationString(Connection.TransportType transportType) { return generalConfiguration() - + newLine - + ovpnGatewayConfiguration() - + newLine - + secretsConfiguration() - + newLine - + androidCustomizations(); + + newLine + + gatewayConfiguration(transportType) + + newLine + + androidCustomizations() + + newLine + + secretsConfiguration(); } - private VpnProfile createOvpnProfile() throws IOException, ConfigParser.ConfigParseError { - String configuration = getConfigurationString(); + private VpnProfile createProfile(Connection.TransportType transportType) throws IOException, ConfigParser.ConfigParseError, JSONException { + String configuration = getConfigurationString(transportType); icsOpenvpnConfigParser.parseConfig(new StringReader(configuration)); - return icsOpenvpnConfigParser.convertProfile(); + if (transportType == OBFS4) { + icsOpenvpnConfigParser.setDispatcherOptions(getDispatcherOptions()); + } + return icsOpenvpnConfigParser.convertProfile(transportType); + } + + private DispatcherOptions getDispatcherOptions() throws JSONException { + JSONObject transportOptions = obfs4Transport.getJSONObject(OPTIONS); + String iatMode = transportOptions.getString("iat-mode"); + String cert = transportOptions.getString("cert"); + String port = obfs4Transport.getJSONArray(PORTS).getString(0); + String ip = gateway.getString(IP_ADDRESS); + return new DispatcherOptions(ip, port, cert, iatMode); } private String generalConfiguration() { @@ -161,21 +151,21 @@ public class VpnConfigGenerator { return commonOptions; } - private String ovpnGatewayConfiguration() { + private String gatewayConfiguration(Connection.TransportType transportType) { String remotes = ""; StringBuilder stringBuilder = new StringBuilder(); try { String ipAddress = gateway.getString(IP_ADDRESS); JSONObject capabilities = gateway.getJSONObject(CAPABILITIES); - JSONArray transports = capabilities.getJSONArray(TRANSPORT); switch (apiVersion) { default: case 1: - ovpnGatewayConfigApiv1(stringBuilder, ipAddress, capabilities); + gatewayConfigApiv1(stringBuilder, ipAddress, capabilities); break; case 2: - ovpnGatewayConfigApiv2(stringBuilder, ipAddress, transports); + JSONArray transports = capabilities.getJSONArray(TRANSPORT); + gatewayConfigApiv2(transportType, stringBuilder, ipAddress, transports); break; } } catch (JSONException e) { @@ -190,10 +180,17 @@ public class VpnConfigGenerator { return remotes; } - private void ovpnGatewayConfigApiv1(StringBuilder stringBuilder, String ipAddress, JSONObject capabilities) throws JSONException { + private void gatewayConfigApiv2(Connection.TransportType transportType, StringBuilder stringBuilder, String ipAddress, JSONArray transports) throws JSONException { + if (transportType == OBFS4) { + obfs4GatewayConfigApiv2(stringBuilder, ipAddress, transports); + } else { + ovpnGatewayConfigApi2(stringBuilder, ipAddress, transports); + } + } + + private void gatewayConfigApiv1(StringBuilder stringBuilder, String ipAddress, JSONObject capabilities) throws JSONException { int port; String protocol; - JSONArray ports = capabilities.getJSONArray(PORTS); for (int i = 0; i < ports.length(); i++) { port = ports.getInt(i); @@ -206,27 +203,41 @@ public class VpnConfigGenerator { } } - private void ovpnGatewayConfigApiv2(StringBuilder stringBuilder, String ipAddress, JSONArray transports) throws JSONException { + private void ovpnGatewayConfigApi2(StringBuilder stringBuilder, String ipAddress, JSONArray transports) throws JSONException { String port; String protocol; + JSONObject openvpnTransport = getTransport(transports, OPENVPN); + JSONArray ports = openvpnTransport.getJSONArray(PORTS); + for (int j = 0; j < ports.length(); j++) { + port = ports.getString(j); + JSONArray protocols = openvpnTransport.getJSONArray(PROTOCOLS); + for (int k = 0; k < protocols.length(); k++) { + protocol = protocols.optString(k); + String newRemote = REMOTE + " " + ipAddress + " " + port + " " + protocol + newLine; + stringBuilder.append(newRemote); + } + } + } + + private JSONObject getTransport(JSONArray transports, Connection.TransportType transportType) throws JSONException { + JSONObject selectedTransport = new JSONObject(); for (int i = 0; i < transports.length(); i++) { JSONObject transport = transports.getJSONObject(i); - if (!transport.getString(TYPE).equals("openvpn")) { - continue; - } - JSONArray ports = transport.getJSONArray(PORTS); - for (int j = 0; j < ports.length(); j++) { - port = ports.getString(j); - JSONArray protocols = transport.getJSONArray(PROTOCOLS); - for (int k = 0; k < protocols.length(); k++) { - protocol = protocols.optString(k); - String newRemote = REMOTE + " " + ipAddress + " " + port + " " + protocol + newLine; - stringBuilder.append(newRemote); - } + if (transport.getString(TYPE).equals(transportType.toString())) { + selectedTransport = transport; + break; } } + return selectedTransport; } + private void obfs4GatewayConfigApiv2(StringBuilder stringBuilder, String ipAddress, JSONArray transports) throws JSONException { + JSONObject obfs4Transport = getTransport(transports, OBFS4); + String route = "route " + ipAddress + " 255.255.255.255 net_gateway" + newLine; + stringBuilder.append(route); + String remote = REMOTE + " " + DISPATCHER_IP + " " + DISPATCHER_PORT + " " + obfs4Transport.getJSONArray(PROTOCOLS).getString(0) + newLine; + stringBuilder.append(remote); + } private String secretsConfiguration() { try { diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Dispatcher.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Dispatcher.java index 05ce2256..240dae75 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Dispatcher.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Dispatcher.java @@ -18,13 +18,13 @@ package se.leap.bitmaskclient.pluggableTransports; import android.content.Context; import android.support.annotation.WorkerThread; +import android.text.TextUtils; import android.util.Log; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; -import java.util.NoSuchElementException; import java.util.StringTokenizer; @@ -34,7 +34,8 @@ import java.util.StringTokenizer; public class Dispatcher { private static final String ASSET_KEY = "piedispatcher"; - private static final String DISPATCHER_PORT = "4430"; + public static final String DISPATCHER_PORT = "4430"; + public static final String DISPATCHER_IP = "127.0.0.1"; private static final String TAG = Dispatcher.class.getName(); private final String remoteIP; private final String remotePort; @@ -43,14 +44,14 @@ public class Dispatcher { private File fileDispatcher; private Context context; private Thread dispatcherThread = null; - private int dipatcherPid = -1; + private int dispatcherPid = -1; - public Dispatcher(Context context, String remoteIP, String remotePort, String certificate, String iatMode) { + public Dispatcher(Context context, DispatcherOptions dispatcherOptions) { this.context = context.getApplicationContext(); - this.remoteIP = remoteIP; - this.remotePort = remotePort; - this.certificate = certificate; - this.iatMode = iatMode; + this.remoteIP = dispatcherOptions.remoteIP; + this.remotePort = dispatcherOptions.remotePort; + this.certificate = dispatcherOptions.cert; + this.iatMode = dispatcherOptions.iatMode; } @WorkerThread @@ -70,7 +71,7 @@ public class Dispatcher { " -transports obfs4" + " -options \"" + String.format("{\\\"cert\\\": \\\"%s\\\", \\\"iatMode\\\": \\\"%s\\\"}\"", certificate, iatMode) + " -logLevel DEBUG -enableLogging" + - " -proxylistenaddr 127.0.0.1:" + DISPATCHER_PORT; + " -proxylistenaddr "+ DISPATCHER_IP + ":" + DISPATCHER_PORT; Log.d(TAG, "dispatcher command: " + dispatcherCommand); runBlockingCmd(new String[]{dispatcherCommand}, dispatcherLog); @@ -82,14 +83,22 @@ public class Dispatcher { }); dispatcherThread.start(); - // get pid of dispatcher + // get pid of dispatcher, try several times in case the dispatcher + // process is not spawned yet StringBuilder log = new StringBuilder(); String pidCommand = "ps | grep piedispatcher"; - runBlockingCmd(new String[]{pidCommand}, log); + for (int i = 0; i < 5; i++) { + runBlockingCmd(new String[]{pidCommand}, log); + if (!TextUtils.isEmpty(log)) { + break; + } + Thread.sleep(100); + } + String output = log.toString(); StringTokenizer st = new StringTokenizer(output, " "); st.nextToken(); // proc owner - dipatcherPid = Integer.parseInt(st.nextToken().trim()); + dispatcherPid = Integer.parseInt(st.nextToken().trim()); } catch(Exception e){ if (dispatcherThread.isAlive()) { Log.e(TAG, e.getMessage() + ". Shutting down Dispatcher thread."); @@ -106,7 +115,7 @@ public class Dispatcher { Log.d(TAG, "Shutting down Dispatcher thread."); if (dispatcherThread != null && dispatcherThread.isAlive()) { try { - killProcess(dipatcherPid); + killProcess(dispatcherPid); } catch (Exception e) { e.printStackTrace(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/DispatcherOptions.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/DispatcherOptions.java new file mode 100644 index 00000000..76ccbd79 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/DispatcherOptions.java @@ -0,0 +1,18 @@ +package se.leap.bitmaskclient.pluggableTransports; + +import java.io.Serializable; + +public class DispatcherOptions implements Serializable { + public String cert; + public String iatMode; + public String remoteIP; + public String remotePort; + + public DispatcherOptions(String remoteIP, String remotePort, String cert, String iatMode) { + this.cert = cert; + this.iatMode = iatMode; + this.remoteIP = remoteIP; + this.remotePort = remotePort; + } + +} -- cgit v1.2.3