diff options
Diffstat (limited to 'app/src')
30 files changed, 842 insertions, 261 deletions
diff --git a/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java b/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java index b67f6fa5..a111e907 100644 --- a/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java +++ b/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java @@ -136,15 +136,14 @@ public class ProviderApiManager extends ProviderApiManagerBase { private Bundle getAndSetProviderJson(Provider provider, boolean dangerOn) { Bundle result = new Bundle(); - String caCert = provider.getCaCert(); JSONObject providerDefinition = provider.getDefinition(); String providerMainUrl = provider.getMainUrlString(); String providerDotJsonString; - if(providerDefinition.length() == 0 || caCert.isEmpty()) + if(providerDefinition.length() == 0 || provider.getCaCert().isEmpty()) providerDotJsonString = downloadWithCommercialCA(providerMainUrl + "/provider.json", dangerOn); else - providerDotJsonString = downloadFromApiUrlWithProviderCA("/provider.json", caCert, provider, dangerOn); + providerDotJsonString = downloadFromApiUrlWithProviderCA("/provider.json", provider, dangerOn); if (ConfigHelper.checkErroneousDownload(providerDotJsonString) || !isValidJson(providerDotJsonString)) { setErrorResult(result, malformed_url, null); @@ -230,6 +229,44 @@ public class ProviderApiManager extends ProviderApiManagerBase { return result; } + /** + * Fetches the Geo ip Json, containing a list of gateways sorted by distance from the users current location + * + * @param provider + * @return + */ + @Override + protected Bundle getGeoIPJson(Provider provider) { + Bundle result = new Bundle(); + + if (!provider.shouldUpdateGeoIpJson() || provider.getGeoipUrl().isDefault()) { + result.putBoolean(BROADCAST_RESULT_KEY, false); + return result; + } + + + try { + URL geoIpUrl = provider.getGeoipUrl().getUrl(); + + String geoipJsonString = downloadFromUrlWithProviderCA(geoIpUrl.toString(), provider, lastDangerOn); + JSONObject geoipJson = new JSONObject(geoipJsonString); + + if (geoipJson.has(ERRORS)) { + result.putBoolean(BROADCAST_RESULT_KEY, false); + } else { + provider.setGeoIpJson(geoipJson); + provider.setLastEipServiceUpdate(System.currentTimeMillis()); + result.putBoolean(BROADCAST_RESULT_KEY, true); + } + + + } catch (JSONException | NullPointerException e) { + result.putBoolean(BROADCAST_RESULT_KEY, false); + e.printStackTrace(); + } + return result; + } + private Bundle downloadCACert(Provider provider, boolean dangerOn) { Bundle result = new Bundle(); @@ -291,16 +328,21 @@ public class ProviderApiManager extends ProviderApiManagerBase { return responseString; } - private String downloadFromApiUrlWithProviderCA(String path, String caCert, Provider provider, boolean dangerOn) { + private String downloadFromApiUrlWithProviderCA(String path, Provider provider, boolean dangerOn) { + String baseUrl = provider.getApiUrlString(); + String urlString = baseUrl + path; + + return downloadFromUrlWithProviderCA(urlString, provider, dangerOn); + } + + private String downloadFromUrlWithProviderCA(String urlString, Provider provider, boolean dangerOn) { String responseString; JSONObject errorJson = new JSONObject(); - String baseUrl = provider.getApiUrlString(); - OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(caCert, errorJson); + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), errorJson); if (okHttpClient == null) { return errorJson.toString(); } - String urlString = baseUrl + path; List<Pair<String, String>> headerArgs = getAuthorizationHeader(); responseString = sendGetStringToServer(urlString, headerArgs, okHttpClient); diff --git a/app/src/main/java/se/leap/bitmaskclient/Constants.java b/app/src/main/java/se/leap/bitmaskclient/Constants.java index 6ec3076c..6462b663 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Constants.java +++ b/app/src/main/java/se/leap/bitmaskclient/Constants.java @@ -142,4 +142,5 @@ public interface Constants { String LOCATION = "location"; String OPENVPN_CONFIGURATION = "openvpn_configuration"; String GATEWAYS = "gateways"; + String HOST = "host"; } diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java index b8883f77..fceadd88 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java @@ -27,14 +27,6 @@ import android.graphics.ColorMatrixColorFilter; import android.os.Bundle; import android.os.IBinder; import android.os.Vibrator; -import androidx.annotation.NonNull; -import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentTransaction; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.widget.AppCompatButton; -import androidx.appcompat.widget.AppCompatImageView; -import androidx.appcompat.widget.AppCompatTextView; import android.text.TextUtils; import android.util.Log; import android.view.Gravity; @@ -44,6 +36,15 @@ import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.AppCompatButton; +import androidx.appcompat.widget.AppCompatImageView; +import androidx.appcompat.widget.AppCompatTextView; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentTransaction; + import java.util.Observable; import java.util.Observer; @@ -62,6 +63,8 @@ import static android.view.View.GONE; import static android.view.View.VISIBLE; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NONETWORK; import static se.leap.bitmaskclient.Constants.ASK_TO_CANCEL_VPN; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; +import static se.leap.bitmaskclient.Constants.EIP_EARLY_ROUTES; import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP; @@ -71,6 +74,7 @@ import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.EipSetupObserver.connectionRetry; import static se.leap.bitmaskclient.EipSetupObserver.gatewayOrder; import static se.leap.bitmaskclient.EipSetupObserver.reconnectingWithDifferentGateway; +import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_GEOIP_JSON; import static se.leap.bitmaskclient.ProviderAPI.UPDATE_INVALID_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.USER_MESSAGE; import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message; @@ -301,7 +305,14 @@ public class EipFragment extends Fragment implements Observer { Log.e(TAG, "context is null when trying to start VPN"); return; } - EipCommand.startVPN(context.getApplicationContext(), false); + if (!provider.getGeoipUrl().isDefault() && provider.shouldUpdateGeoIpJson()) { + Bundle bundle = new Bundle(); + bundle.putBoolean(EIP_ACTION_START, true); + bundle.putBoolean(EIP_EARLY_ROUTES, false); + ProviderAPICommand.execute(getContext().getApplicationContext(), DOWNLOAD_GEOIP_JSON, bundle, provider); + } else { + EipCommand.startVPN(context.getApplicationContext(), false); + } vpnStateImage.showProgress(); routedText.setVisibility(GONE); vpnRoute.setVisibility(GONE); diff --git a/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java index ee7e7ef5..7504e0c0 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java +++ b/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java @@ -6,9 +6,10 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.Bundle; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import android.util.Log; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + import org.json.JSONObject; import java.util.Vector; @@ -39,11 +40,14 @@ import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; import static se.leap.bitmaskclient.Constants.EIP_ACTION_PREPARE_VPN; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_ALWAYS_ON_VPN; +import static se.leap.bitmaskclient.Constants.EIP_EARLY_ROUTES; import static se.leap.bitmaskclient.Constants.EIP_REQUEST; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_PROFILE; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE; +import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_GEOIP_JSON; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_GEOIP_JSON; /** * Created by cyberta on 05.12.18. @@ -151,6 +155,15 @@ class EipSetupObserver extends BroadcastReceiver implements VpnStatus.StateListe PreferenceHelper.storeProviderInPreferences(preferences, provider); EipCommand.startVPN(context.getApplicationContext(), true); break; + case CORRECTLY_DOWNLOADED_GEOIP_JSON: + provider = resultData.getParcelable(PROVIDER_KEY); + ProviderObservable.getInstance().updateProvider(provider); + PreferenceHelper.storeProviderInPreferences(preferences, provider); + maybeStartEipService(resultData); + break; + case INCORRECTLY_DOWNLOADED_GEOIP_JSON: + maybeStartEipService(resultData); + break; default: break; } @@ -160,6 +173,13 @@ class EipSetupObserver extends BroadcastReceiver implements VpnStatus.StateListe } } + private void maybeStartEipService(Bundle resultData) { + if (resultData.getBoolean(EIP_ACTION_START)) { + boolean earlyRoutes = resultData.getBoolean(EIP_EARLY_ROUTES); + EipCommand.startVPN(context.getApplicationContext(), earlyRoutes); + } + } + private void handleEipEvent(Intent intent) { int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED); @@ -184,6 +204,7 @@ class EipSetupObserver extends BroadcastReceiver implements VpnStatus.StateListe finishGatewaySetup(false); EipCommand.startBlockingVPN(context.getApplicationContext()); } else { + //FIXME: finishGatewaySetup(false); EipCommand.stopVPN(context); EipStatus.refresh(); diff --git a/app/src/main/java/se/leap/bitmaskclient/Provider.java b/app/src/main/java/se/leap/bitmaskclient/Provider.java index 4dd7c76e..186ce11e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Provider.java +++ b/app/src/main/java/se/leap/bitmaskclient/Provider.java @@ -18,7 +18,6 @@ package se.leap.bitmaskclient; import android.os.Parcel; import android.os.Parcelable; -import androidx.annotation.NonNull; import com.google.gson.Gson; @@ -46,10 +45,13 @@ import static se.leap.bitmaskclient.ProviderAPI.ERRORS; public final class Provider implements Parcelable { private static long EIP_SERVICE_TIMEOUT = 1000 * 60 * 60 * 24 * 3; + private static long GEOIP_SERVICE_TIMEOUT = 1000 * 60 * 60; private JSONObject definition = new JSONObject(); // Represents our Provider's provider.json private JSONObject eipServiceJson = new JSONObject(); + private JSONObject geoIpJson = new JSONObject(); private DefaultedURL mainUrl = new DefaultedURL(); private DefaultedURL apiUrl = new DefaultedURL(); + private DefaultedURL geoipUrl = new DefaultedURL(); private String providerIp = ""; private String providerApiIp = ""; private String certificatePin = ""; @@ -59,6 +61,7 @@ public final class Provider implements Parcelable { private String privateKey = ""; private String vpnCertificate = ""; private long lastEipServiceUpdate = 0L; + private long lastGeoIpUpdate = 0L; private boolean allowAnonymous; private boolean allowRegistered; @@ -78,21 +81,31 @@ public final class Provider implements Parcelable { DOMAIN = "domain", MAIN_URL = "main_url", PROVIDER_IP = "provider_ip", - PROVIDER_API_IP = "provider_api_ip"; + PROVIDER_API_IP = "provider_api_ip", + GEOIP_URL = "geoip_url"; private static final String API_TERM_NAME = "name"; public Provider() { } public Provider(String mainUrl) { + this(mainUrl, null); + } + + public Provider(String mainUrl, String geoipUrl) { try { this.mainUrl.setUrl(new URL(mainUrl)); } catch (MalformedURLException e) { this.mainUrl = new DefaultedURL(); } + setGeoipUrl(geoipUrl); } public Provider(String mainUrl, String providerIp, String providerApiIp) { + this(mainUrl, null, providerIp, providerApiIp); + } + + public Provider(String mainUrl, String geoipUrl, String providerIp, String providerApiIp) { try { this.mainUrl.setUrl(new URL(mainUrl)); if (providerIp != null) { @@ -103,21 +116,14 @@ public final class Provider implements Parcelable { } } catch (MalformedURLException e) { e.printStackTrace(); + return; } + setGeoipUrl(geoipUrl); } - public Provider(String mainUrl, String providerIp, String providerApiIp, String caCert, String definition) { - try { - this.mainUrl.setUrl(new URL(mainUrl)); - } catch (MalformedURLException e) { - e.printStackTrace(); - } - if (this.providerIp != null) { - this.providerIp = providerIp; - } - if (this.providerApiIp != null) { - this.providerApiIp = providerApiIp; - } + + public Provider(String mainUrl, String geoipUrl, String providerIp, String providerApiIp, String caCert, String definition) { + this(mainUrl, geoipUrl, providerIp, providerApiIp); if (caCert != null) { this.caCert = caCert; } @@ -240,6 +246,18 @@ public final class Provider implements Parcelable { protected DefaultedURL getApiUrl() { return apiUrl; } + + public DefaultedURL getGeoipUrl() { + return geoipUrl; + } + + public void setGeoipUrl(String url) { + try { + this.geoipUrl.setUrl(new URL(url)); + } catch (MalformedURLException e) { + this.geoipUrl = new DefaultedURL(); + } + } protected String getApiUrlWithVersion() { return getApiUrlString() + "/" + getApiVersion(); @@ -262,6 +280,10 @@ public final class Provider implements Parcelable { return definition != null && definition.length() > 0; } + public boolean hasGeoIpJson() { + return geoIpJson != null && geoIpJson.length() > 0; + } + public String getCaCert() { return caCert; @@ -321,56 +343,17 @@ public final class Provider implements Parcelable { parcel.writeString(getMainUrlString()); parcel.writeString(getProviderIp()); parcel.writeString(getProviderApiIp()); + parcel.writeString(getGeoipUrl().toString()); parcel.writeString(getDefinitionString()); parcel.writeString(getCaCert()); parcel.writeString(getEipServiceJsonString()); + parcel.writeString(getGeoIpJsonString()); parcel.writeString(getPrivateKey()); parcel.writeString(getVpnCertificate()); parcel.writeLong(lastEipServiceUpdate); + parcel.writeLong(lastGeoIpUpdate); } - @Override - public boolean equals(Object o) { - if (o instanceof Provider) { - Provider p = (Provider) o; - return p.getDomain().equals(getDomain()) && - definition.toString().equals(p.getDefinition().toString()) && - eipServiceJson.toString().equals(p.getEipServiceJson().toString())&& - mainUrl.equals(p.getMainUrl()) && - providerIp.equals(p.getProviderIp()) && - providerApiIp.equals(p.getProviderApiIp()) && - apiUrl.equals(p.getApiUrl()) && - certificatePin.equals(p.getCertificatePin()) && - certificatePinEncoding.equals(p.getCertificatePinEncoding()) && - caCert.equals(p.getCaCert()) && - apiVersion.equals(p.getApiVersion()) && - privateKey.equals(p.getPrivateKey()) && - vpnCertificate.equals(p.getVpnCertificate()) && - allowAnonymous == p.allowsAnonymous() && - allowRegistered == p.allowsRegistered() && - lastEipServiceUpdate == p.getLastEipServiceUpdate(); - } else return false; - } - - public JSONObject toJson() { - JSONObject json = new JSONObject(); - try { - json.put(Provider.MAIN_URL, mainUrl); - } catch (JSONException e) { - e.printStackTrace(); - } - return json; - } - - @Override - public int hashCode() { - return getDomain().hashCode(); - } - - @Override - public String toString() { - return new Gson().toJson(this); - } //TODO: write a test for marshalling! private Provider(Parcel in) { @@ -386,6 +369,10 @@ public final class Provider implements Parcelable { } tmpString = in.readString(); if (!tmpString.isEmpty()) { + geoipUrl.setUrl(new URL(tmpString)); + } + tmpString = in.readString(); + if (!tmpString.isEmpty()) { definition = new JSONObject((tmpString)); parseDefinition(definition); } @@ -399,6 +386,10 @@ public final class Provider implements Parcelable { } tmpString = in.readString(); if (!tmpString.isEmpty()) { + this.setGeoIpJson(new JSONObject(tmpString)); + } + tmpString = in.readString(); + if (!tmpString.isEmpty()) { this.setPrivateKey(tmpString); } tmpString = in.readString(); @@ -406,11 +397,57 @@ public final class Provider implements Parcelable { this.setVpnCertificate(tmpString); } this.lastEipServiceUpdate = in.readLong(); + this.lastGeoIpUpdate = in.readLong(); } catch (MalformedURLException | JSONException e) { e.printStackTrace(); } } + + @Override + public boolean equals(Object o) { + if (o instanceof Provider) { + Provider p = (Provider) o; + return p.getDomain().equals(getDomain()) && + definition.toString().equals(p.getDefinition().toString()) && + eipServiceJson.toString().equals(p.getEipServiceJsonString()) && + geoIpJson.toString().equals(p.getGeoIpJsonString()) && + providerIp.equals(p.getProviderIp()) && + providerApiIp.equals(p.getProviderApiIp()) && + apiUrl.equals(p.getApiUrl()) && + geoipUrl.equals(p.getGeoipUrl()) && + certificatePin.equals(p.getCertificatePin()) && + certificatePinEncoding.equals(p.getCertificatePinEncoding()) && + caCert.equals(p.getCaCert()) && + apiVersion.equals(p.getApiVersion()) && + privateKey.equals(p.getPrivateKey()) && + vpnCertificate.equals(p.getVpnCertificate()) && + allowAnonymous == p.allowsAnonymous() && + allowRegistered == p.allowsRegistered(); + } else return false; + } + + + public JSONObject toJson() { + JSONObject json = new JSONObject(); + try { + json.put(Provider.MAIN_URL, mainUrl); + } catch (JSONException e) { + e.printStackTrace(); + } + return json; + } + + @Override + public int hashCode() { + return getDomain().hashCode(); + } + + @Override + public String toString() { + return new Gson().toJson(this); + } + private boolean parseDefinition(JSONObject definition) { try { String pin = definition.getString(CA_CERT_FINGERPRINT); @@ -442,14 +479,20 @@ public final class Provider implements Parcelable { lastEipServiceUpdate = timestamp; } - public long getLastEipServiceUpdate() { - return lastEipServiceUpdate; - } - public boolean shouldUpdateEipServiceJson() { return System.currentTimeMillis() - lastEipServiceUpdate >= EIP_SERVICE_TIMEOUT; } + + public void setLastGeoIpUpdate(long timestamp) { + lastGeoIpUpdate = timestamp; + } + + public boolean shouldUpdateGeoIpJson() { + return System.currentTimeMillis() - lastGeoIpUpdate >= GEOIP_SERVICE_TIMEOUT; + } + + public boolean setEipServiceJson(JSONObject eipServiceJson) { if (eipServiceJson.has(ERRORS)) { return false; @@ -458,16 +501,34 @@ public final class Provider implements Parcelable { return true; } + public boolean setGeoIpJson(JSONObject geoIpJson) { + if (geoIpJson.has(ERRORS)) { + return false; + } + this.geoIpJson = geoIpJson; + return true; + } + public JSONObject getEipServiceJson() { return eipServiceJson; } + public JSONObject getGeoIpJson() { + return geoIpJson; + } + + public String getGeoIpJsonString() { + return geoIpJson.toString(); + } + public String getEipServiceJsonString() { return getEipServiceJson().toString(); } + public boolean isDefault() { return getMainUrl().isDefault() && getApiUrl().isDefault() && + getGeoipUrl().isDefault() && certificatePin.isEmpty() && certificatePinEncoding.isEmpty() && caCert.isEmpty(); @@ -515,6 +576,7 @@ public final class Provider implements Parcelable { public void reset() { definition = new JSONObject(); eipServiceJson = new JSONObject(); + geoIpJson = new JSONObject(); apiUrl = new DefaultedURL(); certificatePin = ""; certificatePinEncoding = ""; @@ -524,5 +586,7 @@ public final class Provider implements Parcelable { vpnCertificate = ""; allowRegistered = false; allowAnonymous = false; + lastGeoIpUpdate = 0L; + lastEipServiceUpdate = 0L; } } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java index 9d34b38f..767e6a78 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java @@ -24,6 +24,7 @@ import android.content.SharedPreferences; import androidx.annotation.NonNull; import androidx.core.app.JobIntentService; import androidx.localbroadcastmanager.content.LocalBroadcastManager; + import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; /** @@ -48,6 +49,7 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB TAG = ProviderAPI.class.getSimpleName(), SET_UP_PROVIDER = "setUpProvider", UPDATE_PROVIDER_DETAILS = "updateProviderDetails", + DOWNLOAD_GEOIP_JSON = "downloadGeoIpJson", SIGN_UP = "srpRegister", LOG_IN = "srpAuth", LOG_OUT = "logOut", @@ -77,7 +79,9 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB CORRECTLY_DOWNLOADED_EIP_SERVICE = 13, INCORRECTLY_DOWNLOADED_EIP_SERVICE = 14, CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE = 15, - INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE = 16; + INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE = 16, + CORRECTLY_DOWNLOADED_GEOIP_JSON = 17, + INCORRECTLY_DOWNLOADED_GEOIP_JSON = 18; ProviderApiManager providerApiManager; @@ -99,11 +103,12 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB */ static void enqueueWork(Context context, Intent work) { try { - enqueueWork(context, ProviderAPI.class, JOB_ID, work); + ProviderAPI.enqueueWork(context, ProviderAPI.class, JOB_ID, work); } catch (IllegalStateException e) { e.printStackTrace(); } } + @Override protected void onHandleWork(@NonNull Intent command) { providerApiManager.handleIntent(command); diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java index f3122376..9a0731cd 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPICommand.java @@ -4,11 +4,13 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.ResultReceiver; +import android.util.Log; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class ProviderAPICommand { + private static final String TAG = ProviderAPICommand.class.getSimpleName(); private Context context; private String action; @@ -60,22 +62,22 @@ public class ProviderAPICommand { return command; } - public static void execute(Context context, String action, Provider provider) { + public static void execute(Context context, String action, @NotNull Provider provider) { ProviderAPICommand command = new ProviderAPICommand(context, action, provider); command.execute(); } - public static void execute(Context context, String action, Bundle parameters, Provider provider) { + public static void execute(Context context, String action, Bundle parameters, @NotNull Provider provider) { ProviderAPICommand command = new ProviderAPICommand(context, action, parameters, provider); command.execute(); } - public static void execute(Context context, String action, Bundle parameters, Provider provider, ResultReceiver resultReceiver) { + public static void execute(Context context, String action, Bundle parameters, @NotNull Provider provider, ResultReceiver resultReceiver) { ProviderAPICommand command = new ProviderAPICommand(context, action, parameters, provider, resultReceiver); command.execute(); } - public static void execute(Context context, String action, Provider provider, ResultReceiver resultReceiver) { + public static void execute(Context context, String action, @NotNull Provider provider, ResultReceiver resultReceiver) { ProviderAPICommand command = new ProviderAPICommand(context, action, provider, resultReceiver); command.execute(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java index 1fb6bf48..e5ca184f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java @@ -22,11 +22,12 @@ import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; import android.os.ResultReceiver; -import androidx.annotation.NonNull; import android.util.Base64; import android.util.Log; import android.util.Pair; +import androidx.annotation.NonNull; + import org.json.JSONException; import org.json.JSONObject; @@ -60,17 +61,21 @@ import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; import static se.leap.bitmaskclient.Constants.CREDENTIALS_PASSWORD; import static se.leap.bitmaskclient.Constants.CREDENTIALS_USERNAME; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.Provider.CA_CERT; +import static se.leap.bitmaskclient.Provider.GEOIP_URL; import static se.leap.bitmaskclient.Provider.PROVIDER_API_IP; import static se.leap.bitmaskclient.Provider.PROVIDER_IP; import static se.leap.bitmaskclient.ProviderAPI.BACKEND_ERROR_KEY; import static se.leap.bitmaskclient.ProviderAPI.BACKEND_ERROR_MESSAGE; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE; +import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_GEOIP_JSON; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_GEOIP_JSON; import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_SERVICE_JSON; import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.ERRORID; @@ -78,6 +83,7 @@ import static se.leap.bitmaskclient.ProviderAPI.ERRORS; import static se.leap.bitmaskclient.ProviderAPI.FAILED_LOGIN; import static se.leap.bitmaskclient.ProviderAPI.FAILED_SIGNUP; import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_GEOIP_JSON; import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE; import static se.leap.bitmaskclient.ProviderAPI.LOGOUT_FAILED; @@ -143,7 +149,11 @@ public abstract class ProviderApiManagerBase { } public void handleIntent(Intent command) { - final ResultReceiver receiver = command.getParcelableExtra(RECEIVER_KEY); +// Log.d(TAG, "handleIntent was called!"); + ResultReceiver receiver = null; + if (command.getParcelableExtra(RECEIVER_KEY) != null) { + receiver = command.getParcelableExtra(RECEIVER_KEY); + } String action = command.getAction(); Bundle parameters = command.getBundleExtra(PARAMETERS); @@ -167,6 +177,7 @@ public abstract class ProviderApiManagerBase { Bundle task = new Bundle(); result = setUpProvider(provider, task); if (result.getBoolean(BROADCAST_RESULT_KEY)) { + getGeoIPJson(provider); sendToReceiverOrBroadcast(receiver, PROVIDER_OK, result, provider); } else { sendToReceiverOrBroadcast(receiver, PROVIDER_NOK, result, provider); @@ -177,6 +188,7 @@ public abstract class ProviderApiManagerBase { ProviderObservable.getInstance().setProviderForDns(provider); result = setUpProvider(provider, parameters); if (result.getBoolean(BROADCAST_RESULT_KEY)) { + getGeoIPJson(provider); sendToReceiverOrBroadcast(receiver, PROVIDER_OK, result, provider); } else { sendToReceiverOrBroadcast(receiver, PROVIDER_NOK, result, provider); @@ -245,6 +257,20 @@ public abstract class ProviderApiManagerBase { } } break; + + case DOWNLOAD_GEOIP_JSON: + if (!provider.getGeoipUrl().isDefault()) { + boolean startEIP = parameters.getBoolean(EIP_ACTION_START); + ProviderObservable.getInstance().setProviderForDns(provider); + result = getGeoIPJson(provider); + result.putBoolean(EIP_ACTION_START, startEIP); + if (result.getBoolean(BROADCAST_RESULT_KEY)) { + sendToReceiverOrBroadcast(receiver, CORRECTLY_DOWNLOADED_GEOIP_JSON, result, provider); + } else { + sendToReceiverOrBroadcast(receiver, INCORRECTLY_DOWNLOADED_GEOIP_JSON, result, provider); + } + ProviderObservable.getInstance().setProviderForDns(null); + } } } @@ -664,6 +690,15 @@ public abstract class ProviderApiManagerBase { protected abstract Bundle updateVpnCertificate(Provider provider); + /** + * Fetches the Geo ip Json, containing a list of gateways sorted by distance from the users current location + * + * @param provider + * @return + */ + protected abstract Bundle getGeoIPJson(Provider provider); + + protected boolean isValidJson(String jsonString) { try { new JSONObject(jsonString); @@ -708,6 +743,7 @@ public abstract class ProviderApiManagerBase { provider.setVpnCertificate(getPersistedVPNCertificate(providerDomain)); provider.setProviderApiIp(getPersistedProviderApiIp(providerDomain)); provider.setProviderIp(getPersistedProviderIp(providerDomain)); + provider.setGeoipUrl(getPersistedGeoIp(providerDomain)); } } @@ -816,6 +852,10 @@ public abstract class ProviderApiManagerBase { return getFromPersistedProvider(PROVIDER_IP, providerDomain, preferences); } + protected String getPersistedGeoIp(String providerDomain) { + return getFromPersistedProvider(GEOIP_URL, providerDomain, preferences); + } + protected boolean hasUpdatedProviderDetails(String domain) { return preferences.contains(Provider.KEY + "." + domain) && preferences.contains(CA_CERT + "." + domain); } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java index 7c45c921..a7d0f916 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java @@ -106,7 +106,7 @@ public abstract class ProviderListBaseActivity extends ProviderSetupBaseActivity } public void showAndSelectProvider(String newURL) { - provider = new Provider(newURL, "", ""); + provider = new Provider(newURL, null, null); autoSelectProvider(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java index 6d074a7a..c23ad270 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java @@ -20,6 +20,7 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import static se.leap.bitmaskclient.Provider.GEOIP_URL; import static se.leap.bitmaskclient.Provider.MAIN_URL; import static se.leap.bitmaskclient.Provider.PROVIDER_API_IP; import static se.leap.bitmaskclient.Provider.PROVIDER_IP; @@ -90,18 +91,20 @@ public class ProviderManager implements AdapteeCollection<Provider> { String providerApiIp = null; String certificate = null; String providerDefinition = null; + String geoipUrl = null; try { String provider = file.substring(0, file.length() - ".url".length()); InputStream providerFile = assetsManager.open(directory + "/" + file); mainUrl = extractKeyFromInputStream(providerFile, MAIN_URL); providerIp = extractKeyFromInputStream(providerFile, PROVIDER_IP); providerApiIp = extractKeyFromInputStream(providerFile, PROVIDER_API_IP); + geoipUrl = extractKeyFromInputStream(providerFile, GEOIP_URL); certificate = loadInputStreamAsString(assetsManager.open(provider + EXT_PEM)); providerDefinition = loadInputStreamAsString(assetsManager.open(provider + EXT_JSON)); } catch (IOException e) { e.printStackTrace(); } - providers.add(new Provider(mainUrl, providerIp, providerApiIp, certificate, providerDefinition)); + providers.add(new Provider(mainUrl, geoipUrl, providerIp, providerApiIp, certificate, providerDefinition)); } return providers; diff --git a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java index b6ace6db..dd4878f3 100644 --- a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java @@ -21,9 +21,10 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Bundle; +import android.util.Log; + import androidx.annotation.IntDef; import androidx.annotation.Nullable; -import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -40,8 +41,6 @@ import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.MainActivity.ACTION_SHOW_VPN_FRAGMENT; import static se.leap.bitmaskclient.utils.ConfigHelper.isDefaultBitmask; -import static se.leap.bitmaskclient.utils.PreferenceHelper.getSavedProviderFromSharedPreferences; -import static se.leap.bitmaskclient.utils.PreferenceHelper.providerInSharedPreferences; import static se.leap.bitmaskclient.utils.PreferenceHelper.storeProviderInPreferences; /** @@ -162,9 +161,9 @@ public class StartActivity extends Activity{ } private void prepareEIP() { - boolean providerExists = providerInSharedPreferences(preferences); + boolean providerExists = ProviderObservable.getInstance().getCurrentProvider() != null; if (providerExists) { - Provider provider = getSavedProviderFromSharedPreferences(preferences); + Provider provider = ProviderObservable.getInstance().getCurrentProvider(); if(!provider.isConfigured()) { configureLeapProvider(); } else { 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 34c88ef9..e0c96ebb 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -226,7 +226,7 @@ public final class EIP extends JobIntentService implements Observer { return; } - GatewaysManager gatewaysManager = gatewaysFromPreferences(); + GatewaysManager gatewaysManager = new GatewaysManager(getApplicationContext()); if (gatewaysManager.isEmpty()) { setErrorResult(result, warning_client_parsing_error_gateways, null); tellToReceiverOrBroadcast(this, EIP_ACTION_START, RESULT_CANCELED, result); @@ -248,7 +248,7 @@ public final class EIP extends JobIntentService implements Observer { * The {@link OnBootReceiver} will care if there is no profile. */ private void startEIPAlwaysOnVpn() { - GatewaysManager gatewaysManager = gatewaysFromPreferences(); + GatewaysManager gatewaysManager = new GatewaysManager(getApplicationContext()); Gateway gateway = gatewaysManager.select(0); if (!launchActiveGateway(gateway, 0)) { @@ -310,15 +310,6 @@ public final class EIP extends JobIntentService implements Observer { } /** - * read eipServiceJson from preferences and parse Gateways - * - * @return GatewaysManager - */ - private GatewaysManager gatewaysFromPreferences() { - return new GatewaysManager(getApplicationContext(), preferences); - } - - /** * read VPN certificate from preferences and check it * broadcast result */ 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 66c9fe84..f3eea415 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java @@ -34,6 +34,7 @@ import de.blinkt.openvpn.core.ConfigParser; import de.blinkt.openvpn.core.connection.Connection; import se.leap.bitmaskclient.utils.PreferenceHelper; +import static se.leap.bitmaskclient.Constants.HOST; import static se.leap.bitmaskclient.Constants.IP_ADDRESS; import static se.leap.bitmaskclient.Constants.LOCATION; import static se.leap.bitmaskclient.Constants.LOCATIONS; @@ -114,6 +115,10 @@ public class Gateway { return gateway.optString(IP_ADDRESS); } + public String getHost() { + return gateway.optString(HOST); + } + private String locationAsName(JSONObject eipDefinition) { JSONObject location = getLocationInfo(eipDefinition); return location.optString(NAME); @@ -152,6 +157,10 @@ public class Gateway { return vpnProfiles.get(transportType); } + public boolean suppoortsTransport(Connection.TransportType transportType) { + return vpnProfiles.get(transportType) != null; + } + public int getTimezone() { return timezone; } 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 fbe1861a..0515a35e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -17,7 +17,6 @@ package se.leap.bitmaskclient.eip; import android.content.Context; -import android.content.SharedPreferences; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -30,6 +29,7 @@ import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.List; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ConfigParser; @@ -37,7 +37,6 @@ import de.blinkt.openvpn.core.VpnStatus; import de.blinkt.openvpn.core.connection.Connection; import se.leap.bitmaskclient.Provider; import se.leap.bitmaskclient.ProviderObservable; -import se.leap.bitmaskclient.utils.PreferenceHelper; import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4; import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN; @@ -54,15 +53,9 @@ public class GatewaysManager { private static final String TAG = GatewaysManager.class.getSimpleName(); private Context context; - private SharedPreferences preferences; private LinkedHashMap<String, Gateway> gateways = new LinkedHashMap<>(); private Type listType = new TypeToken<ArrayList<Gateway>>() {}.getType(); - - public GatewaysManager(Context context, SharedPreferences preferences) { - this.preferences = preferences; - this.context = context; - configureFromPreferences(); - } + private ArrayList<Gateway> presortedList = new ArrayList<>(); public GatewaysManager(Context context) { configureFromCurrentProvider(); @@ -75,13 +68,42 @@ public class GatewaysManager { */ public Gateway select(int nClosest) { Connection.TransportType transportType = getUsePluggableTransports(context) ? OBFS4 : OPENVPN; - GatewaySelector gatewaySelector = new GatewaySelector(new ArrayList<>(gateways.values())); + + if (presortedList.size() > 0) { + return getGatewayFromPresortedList(nClosest, transportType); + } + + return getGatewayFromTimezoneCalculation(nClosest, transportType); + } + + + private Gateway getGatewayFromTimezoneCalculation(int nClosest, Connection.TransportType transportType) { + List<Gateway> list = new ArrayList<>(gateways.values()); + GatewaySelector gatewaySelector = new GatewaySelector(list); Gateway gateway; - while ((gateway = gatewaySelector.select(nClosest)) != null) { - if (gateway.getProfile(transportType) != null) { - return gateway; + int found = 0; + int i = 0; + while ((gateway = gatewaySelector.select(i)) != null) { + if (gateway.suppoortsTransport(transportType)) { + if (found == nClosest) { + return gateway; + } + found++; + } + i++; + } + return null; + } + + private Gateway getGatewayFromPresortedList(int nClosest, Connection.TransportType transportType) { + int found = 0; + for (Gateway gateway : presortedList) { + if (gateway.suppoortsTransport(transportType)) { + if (found == nClosest) { + return gateway; + } + found++; } - nClosest++; } return null; } @@ -92,15 +114,41 @@ public class GatewaysManager { * @return position of the gateway owning to the profile */ public int getPosition(VpnProfile profile) { - Connection.TransportType transportType = getUsePluggableTransports(context) ? OBFS4 : OPENVPN; + if (presortedList.size() > 0) { + return getPositionFromPresortedList(profile); + } + + return getPositionFromTimezoneCalculatedList(profile); + } + + private int getPositionFromPresortedList(VpnProfile profile) { + Connection.TransportType transportType = profile.mUsePluggableTransports ? OBFS4 : OPENVPN; + int nClosest = 0; + for (Gateway gateway : presortedList) { + if (gateway.suppoortsTransport(transportType)) { + if (profile.equals(gateway.getProfile(transportType))) { + return nClosest; + } + nClosest++; + } + } + return -1; + } + + private int getPositionFromTimezoneCalculatedList(VpnProfile profile) { + Connection.TransportType transportType = profile.mUsePluggableTransports ? OBFS4 : OPENVPN; GatewaySelector gatewaySelector = new GatewaySelector(new ArrayList<>(gateways.values())); Gateway gateway; int nClosest = 0; - while ((gateway = gatewaySelector.select(nClosest)) != null) { - if (profile.equals(gateway.getProfile(transportType))) { - return nClosest; + int i = 0; + while ((gateway = gatewaySelector.select(i)) != null) { + if (gateway.suppoortsTransport(transportType)) { + if (profile.equals(gateway.getProfile(transportType))) { + return nClosest; + } + nClosest++; } - nClosest++; + i++; } return -1; } @@ -126,41 +174,56 @@ public class GatewaysManager { } /** - * parse gateways from eipDefinition - * @param eipDefinition eipServiceJson + * parse gateways from Provider's eip service + * @param provider */ - private void fromEipServiceJson(JSONObject eipDefinition, JSONObject secrets) { - JSONArray gatewaysDefined = new JSONArray(); - try { - gatewaysDefined = eipDefinition.getJSONArray(GATEWAYS); - } catch (Exception e) { - e.printStackTrace(); - } - - for (int i = 0; i < gatewaysDefined.length(); i++) { - try { - JSONObject gw = gatewaysDefined.getJSONObject(i); - Gateway aux = new Gateway(eipDefinition, secrets, gw, this.context); - if (gateways.get(aux.getRemoteIP()) == null) { - addGateway(aux); - } - } catch (JSONException | ConfigParser.ConfigParseError | IOException e) { - e.printStackTrace(); - VpnStatus.logError("Unable to parse gateway config!"); - } - } + private void parseDefaultGateways(Provider provider) { + try { + JSONObject eipDefinition = provider.getEipServiceJson(); + JSONObject secrets = secretsConfigurationFromCurrentProvider(); + + JSONArray gatewaysDefined = new JSONArray(); + try { + gatewaysDefined = eipDefinition.getJSONArray(GATEWAYS); + } catch (Exception e) { + e.printStackTrace(); + } + + for (int i = 0; i < gatewaysDefined.length(); i++) { + try { + JSONObject gw = gatewaysDefined.getJSONObject(i); + Gateway aux = new Gateway(eipDefinition, secrets, gw, this.context); + if (gateways.get(aux.getHost()) == null) { + addGateway(aux); + } + } catch (JSONException | ConfigParser.ConfigParseError | IOException e) { + e.printStackTrace(); + VpnStatus.logError("Unable to parse gateway config!"); + } + } + } catch (NullPointerException npe) { + npe.printStackTrace(); + } } - private JSONObject secretsConfigurationFromPreferences() { - JSONObject result = new JSONObject(); - try { - result.put(Provider.CA_CERT, preferences.getString(Provider.CA_CERT, "")); - result.put(PROVIDER_PRIVATE_KEY, preferences.getString(PROVIDER_PRIVATE_KEY, "")); - result.put(PROVIDER_VPN_CERTIFICATE, preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); - } catch (JSONException e) { - e.printStackTrace(); - } - return result; + private void parseGatewaysFromGeoIpServiceJson(Provider provider) { + try { + JSONObject geoIpJson = provider.getGeoIpJson(); + JSONArray gatewaylist = geoIpJson.getJSONArray(GATEWAYS); + + for (int i = 0; i < gatewaylist.length(); i++) { + try { + String key = gatewaylist.getString(i); + if (gateways.containsKey(key)) { + presortedList.add(gateways.get(key)); + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + } catch (NullPointerException | JSONException npe) { + npe.printStackTrace(); + } } private JSONObject secretsConfigurationFromCurrentProvider() { @@ -177,31 +240,13 @@ public class GatewaysManager { return result; } - - void clearGateways() { - gateways.clear(); - } - private void addGateway(Gateway gateway) { - gateways.put(gateway.getRemoteIP(), gateway); - } - - /** - * read EipServiceJson from preferences and set gateways - */ - private void configureFromPreferences() { - fromEipServiceJson( - PreferenceHelper.getEipDefinitionFromPreferences(preferences), secretsConfigurationFromPreferences() - ); + gateways.put(gateway.getHost(), gateway); } private void configureFromCurrentProvider() { - try { - JSONObject json = ProviderObservable.getInstance().getCurrentProvider().getEipServiceJson(); - fromEipServiceJson(json, secretsConfigurationFromCurrentProvider()); - } catch (NullPointerException npe) { - npe.printStackTrace(); - } - + Provider provider = ProviderObservable.getInstance().getCurrentProvider(); + parseDefaultGateways(provider); + parseGatewaysFromGeoIpServiceJson(provider); } } diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java index 669b7f8e..cb2aeb26 100644 --- a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java @@ -37,9 +37,6 @@ import static se.leap.bitmaskclient.Constants.USE_PLUGGABLE_TRANSPORTS; */ public class PreferenceHelper { - public static boolean providerInSharedPreferences(@NonNull SharedPreferences preferences) { - return preferences.getBoolean(PROVIDER_CONFIGURED, false); - } public static Provider getSavedProviderFromSharedPreferences(@NonNull SharedPreferences preferences) { Provider provider = new Provider(); @@ -47,6 +44,7 @@ public class PreferenceHelper { provider.setMainUrl(new URL(preferences.getString(Provider.MAIN_URL, ""))); provider.setProviderIp(preferences.getString(Provider.PROVIDER_IP, "")); provider.setProviderApiIp(preferences.getString(Provider.PROVIDER_API_IP, "")); + provider.setGeoipUrl(preferences.getString(Provider.GEOIP_URL, "")); provider.define(new JSONObject(preferences.getString(Provider.KEY, ""))); provider.setCaCert(preferences.getString(Provider.CA_CERT, "")); provider.setVpnCertificate(preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); @@ -68,6 +66,7 @@ public class PreferenceHelper { public static void storeProviderInPreferences(SharedPreferences preferences, Provider provider) { preferences.edit().putBoolean(PROVIDER_CONFIGURED, true). putString(Provider.PROVIDER_IP, provider.getProviderIp()). + putString(Provider.GEOIP_URL, provider.getGeoipUrl().toString()). putString(Provider.PROVIDER_API_IP, provider.getProviderApiIp()). putString(Provider.MAIN_URL, provider.getMainUrlString()). putString(Provider.KEY, provider.getDefinitionString()). @@ -82,6 +81,7 @@ public class PreferenceHelper { putString(Provider.PROVIDER_IP + "." + providerDomain, provider.getProviderIp()). putString(Provider.PROVIDER_API_IP + "." + providerDomain, provider.getProviderApiIp()). putString(Provider.MAIN_URL + "." + providerDomain, provider.getMainUrlString()). + putString(Provider.GEOIP_URL + "." + providerDomain, provider.getGeoipUrl().toString()). putString(Provider.KEY + "." + providerDomain, provider.getDefinitionString()). putString(Provider.CA_CERT + "." + providerDomain, provider.getCaCert()). putString(PROVIDER_EIP_DEFINITION + "." + providerDomain, provider.getEipServiceJsonString()). @@ -114,6 +114,7 @@ public class PreferenceHelper { remove(Provider.PROVIDER_IP + "." + providerDomain). remove(Provider.PROVIDER_API_IP + "." + providerDomain). remove(Provider.MAIN_URL + "." + providerDomain). + remove(Provider.GEOIP_URL + "." + providerDomain). remove(PROVIDER_EIP_DEFINITION + "." + providerDomain). remove(PROVIDER_PRIVATE_KEY + "." + providerDomain). remove(PROVIDER_VPN_CERTIFICATE + "." + providerDomain). diff --git a/app/src/normal/assets/urls/riseup.net.url b/app/src/normal/assets/urls/riseup.net.url index a4a90f90..3c1e6b49 100644 --- a/app/src/normal/assets/urls/riseup.net.url +++ b/app/src/normal/assets/urls/riseup.net.url @@ -1,5 +1,6 @@ { "main_url" : "https://riseup.net", "provider_ip" : "198.252.153.70", - "provider_api_ip" : "198.252.153.107" + "provider_api_ip" : "198.252.153.107", + "geoip_url" : "https://api.black.riseup.net:9001/json" } diff --git a/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java b/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java index ceaeebc0..04ffe9e6 100644 --- a/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java +++ b/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java @@ -20,9 +20,10 @@ package se.leap.bitmaskclient; import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; -import androidx.multidex.BuildConfig; import android.util.Pair; +import androidx.multidex.BuildConfig; + import org.json.JSONException; import org.json.JSONObject; @@ -58,6 +59,8 @@ import static se.leap.bitmaskclient.utils.ConfigHelper.getProviderFormattedStrin public class ProviderApiManager extends ProviderApiManagerBase { + private static final String TAG = ProviderApiManager.class.getSimpleName(); + public ProviderApiManager(SharedPreferences preferences, Resources resources, OkHttpClientGenerator clientGenerator, ProviderApiServiceCallback callback) { super(preferences, resources, clientGenerator, callback); } @@ -120,14 +123,12 @@ public class ProviderApiManager extends ProviderApiManagerBase { private Bundle getAndSetProviderJson(Provider provider) { Bundle result = new Bundle(); - String caCert = provider.getCaCert(); - String providerDotJsonString; - if(provider.getDefinitionString().length() == 0 || caCert.isEmpty()) { + if(provider.getDefinitionString().length() == 0 || provider.getCaCert().isEmpty()) { String providerJsonUrl = provider.getMainUrlString() + "/provider.json"; providerDotJsonString = downloadWithCommercialCA(providerJsonUrl, provider); } else { - providerDotJsonString = downloadFromApiUrlWithProviderCA("/provider.json", caCert, provider); + providerDotJsonString = downloadFromApiUrlWithProviderCA("/provider.json", provider); } if (ConfigHelper.checkErroneousDownload(providerDotJsonString) || !isValidJson(providerDotJsonString)) { @@ -214,6 +215,43 @@ public class ProviderApiManager extends ProviderApiManagerBase { return result; } + /** + * Fetches the Geo ip Json, containing a list of gateways sorted by distance from the users current location + * + * @param provider + * @return + */ + @Override + protected Bundle getGeoIPJson(Provider provider) { + Bundle result = new Bundle(); + + if (!provider.shouldUpdateGeoIpJson() || provider.getGeoipUrl().isDefault()) { + result.putBoolean(BROADCAST_RESULT_KEY, false); + return result; + } + + try { + URL geoIpUrl = provider.getGeoipUrl().getUrl(); + + String geoipJsonString = downloadFromUrlWithProviderCA(geoIpUrl.toString(), provider); + JSONObject geoipJson = new JSONObject(geoipJsonString); + + if (geoipJson.has(ERRORS)) { + result.putBoolean(BROADCAST_RESULT_KEY, false); + } else{ + provider.setGeoIpJson(geoipJson); + provider.setLastGeoIpUpdate(System.currentTimeMillis()); + result.putBoolean(BROADCAST_RESULT_KEY, true); + } + + } catch (JSONException | NullPointerException e) { + result.putBoolean(BROADCAST_RESULT_KEY, false); + e.printStackTrace(); + } + return result; + } + + private Bundle downloadCACert(Provider provider) { Bundle result = new Bundle(); try { @@ -276,30 +314,33 @@ public class ProviderApiManager extends ProviderApiManagerBase { * * @return an empty string if it fails, the response body if not. */ - private String downloadFromApiUrlWithProviderCA(String path, String caCert, Provider provider) { + private String downloadFromApiUrlWithProviderCA(String path, Provider provider) { + String baseUrl = provider.getApiUrlString(); + String urlString = baseUrl + path; + return downloadFromUrlWithProviderCA(urlString, provider); + } + + private String downloadFromUrlWithProviderCA(String urlString, Provider provider) { String responseString; JSONObject errorJson = new JSONObject(); - String baseUrl = provider.getApiUrlString(); - OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(caCert, errorJson); + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), errorJson); if (okHttpClient == null) { return errorJson.toString(); } - String urlString = baseUrl + path; List<Pair<String, String>> headerArgs = getAuthorizationHeader(); responseString = sendGetStringToServer(urlString, headerArgs, okHttpClient); return responseString; - } - /** - * Tries to download the contents of the provided url using not commercially validated CA certificate from chosen provider. - * - * @param urlString as a string - * @return an empty string if it fails, the url content if not. - */ + /** + * Tries to download the contents of the provided url using not commercially validated CA certificate from chosen provider. + * + * @param urlString as a string + * @return an empty string if it fails, the url content if not. + */ private String downloadWithProviderCA(String caCert, String urlString) { JSONObject initError = new JSONObject(); String responseString; diff --git a/app/src/sharedTest/java/se.leap.bitmaskclient/testutils/TestSetupHelper.java b/app/src/sharedTest/java/se.leap.bitmaskclient/testutils/TestSetupHelper.java index c7c85f8d..c741faf2 100644 --- a/app/src/sharedTest/java/se.leap.bitmaskclient/testutils/TestSetupHelper.java +++ b/app/src/sharedTest/java/se.leap.bitmaskclient/testutils/TestSetupHelper.java @@ -51,12 +51,14 @@ public class TestSetupHelper { public static Provider getConfiguredProvider() throws IOException, JSONException { - return getProvider(null, null, null, null, null, null); + return getProvider(null, null, null, null, null, null, null, null); } - public static Provider getProvider(String domain, String providerIp, String providerApiIp, String caCertFile, String providerJson, String eipServiceJson) { + public static Provider getProvider(String domain, String geoipUrl, String providerIp, String providerApiIp, String caCertFile, String providerJson, String eipServiceJson, String geoIpJson) { if (domain == null) domain = "https://riseup.net"; + if (geoipUrl == null) + geoipUrl = "https://api.black.riseup.net:9001/json"; if (providerIp == null) { providerIp = ""; } @@ -70,10 +72,14 @@ public class TestSetupHelper { if (eipServiceJson == null) { eipServiceJson = "riseup.service.json"; } + if (geoIpJson == null) { + geoIpJson = "riseup.geoip.json"; + } try { Provider p = new Provider( domain, + geoipUrl, providerIp, providerApiIp, getInputAsString(TestSetupHelper.class.getClassLoader().getResourceAsStream(caCertFile)), @@ -83,6 +89,10 @@ public class TestSetupHelper { JSONObject eipServiceJsonObject = new JSONObject( getInputAsString(TestSetupHelper.class.getClassLoader().getResourceAsStream(eipServiceJson))); p.setEipServiceJson(eipServiceJsonObject); + + JSONObject geoIpJsonObject = new JSONObject( + getInputAsString(TestSetupHelper.class.getClassLoader().getResourceAsStream(geoIpJson))); + p.setGeoIpJson(geoIpJsonObject); return p; } catch (IOException | JSONException e) { e.printStackTrace(); diff --git a/app/src/test/java/se/leap/bitmaskclient/PreferenceHelperTest.java b/app/src/test/java/se/leap/bitmaskclient/PreferenceHelperTest.java index d49fa08c..edac3480 100644 --- a/app/src/test/java/se/leap/bitmaskclient/PreferenceHelperTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/PreferenceHelperTest.java @@ -6,18 +6,14 @@ import org.junit.Before; import org.junit.Test; import se.leap.bitmaskclient.testutils.MockSharedPreferences; -import se.leap.bitmaskclient.utils.ConfigHelper; -import se.leap.bitmaskclient.utils.PreferenceHelper; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static se.leap.bitmaskclient.Constants.PROVIDER_CONFIGURED; import static se.leap.bitmaskclient.Constants.PROVIDER_EIP_DEFINITION; import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY; import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString; import static se.leap.bitmaskclient.utils.PreferenceHelper.getSavedProviderFromSharedPreferences; -import static se.leap.bitmaskclient.utils.PreferenceHelper.providerInSharedPreferences; /** * Created by cyberta on 17.01.18. @@ -33,17 +29,6 @@ public class PreferenceHelperTest { } @Test - public void providerInSharedPreferences_notInPreferences_returnsFalse() throws Exception { - assertFalse(providerInSharedPreferences(mockPreferences)); - } - - @Test - public void providerInSharedPreferences_inPreferences_returnsTrue() throws Exception { - mockPreferences.edit().putBoolean(PROVIDER_CONFIGURED, true).apply(); - assertTrue(providerInSharedPreferences(mockPreferences)); - } - - @Test public void getSavedProviderFromSharedPreferences_notInPreferences_returnsDefaultProvider() throws Exception { Provider provider = getSavedProviderFromSharedPreferences(mockPreferences); assertFalse(provider.isConfigured()); diff --git a/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java b/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java index 0f9d358e..8a74e5de 100644 --- a/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java @@ -23,13 +23,21 @@ public class ProviderTest { } @Test - public void testEquals_sameFields_returnsFalse() throws Exception { + public void testEquals_differntMainUrl_returnsFalse() throws Exception { Provider p1 = TestSetupHelper.getConfiguredProvider(); Provider p2 = TestSetupHelper.getConfiguredProvider(); p2.setMainUrl("http://somethingsdiffer.org"); assertFalse("Providers should be same:", p1.equals(p2)); } + @Test + public void testEquals_differentGeoIpUrl_returnsFalse() throws Exception { + Provider p1 = TestSetupHelper.getConfiguredProvider(); + Provider p2 = TestSetupHelper.getConfiguredProvider(); + p2.setGeoipUrl(null); + assertFalse("Providers should be same:", p1.equals(p2)); + } + // see ProviderManagerTest testing add(...) @Test public void testEqualsThroughSetContains_differentFields_returnsFalse() throws Exception { @@ -55,7 +63,9 @@ public class ProviderTest { null, null, null, - "ptdemo.bitmask.eip-service.json"); + null, + "ptdemo.bitmask.eip-service.json", + null); assertTrue(p1.supportsPluggableTransports()); } @@ -67,7 +77,9 @@ public class ProviderTest { null, null, null, - "eip-service-two-gateways.json"); + null, + "eip-service-two-gateways.json", + null); assertFalse(p1.supportsPluggableTransports()); } diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java index 417c4bfc..bfe96d2c 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java @@ -74,29 +74,6 @@ public class GatewaysManagerTest { .commit(); } - @Test - public void testFromEipServiceJson_emptyJson() throws Exception { - GatewaysManager gatewaysManager = new GatewaysManager(mockContext, sharedPreferences); - assertEquals(0, gatewaysManager.size()); - } - - @Test - public void testFromEipServiceJson_ignoreGatewaysWithMisconfiguredTransportsWhileAddingValidOnes() throws Exception { - updateEipServiceJson("ptdemo_misconfigured_mixed_gateways.json"); - GatewaysManager gatewaysManager = new GatewaysManager(mockContext, sharedPreferences); - assertEquals(1, gatewaysManager.size()); - assertNull(gatewaysManager.select(0).getProfile(OBFS4)); - assertNotNull(gatewaysManager.select(0).getProfile(Connection.TransportType.OPENVPN)); - } - - @Test - public void testClearGatewaysAndProfiles_resetGateways() throws Exception { - updateEipServiceJson("eip-service-two-gateways.json"); - GatewaysManager gatewaysManager = new GatewaysManager(mockContext, sharedPreferences); - assertEquals(2, gatewaysManager.size()); - gatewaysManager.clearGateways(); - assertEquals(0, gatewaysManager.size()); - } @Test public void testGatewayManagerFromCurrentProvider_noProvider_noGateways() { @@ -107,7 +84,7 @@ public class GatewaysManagerTest { @Test public void testGatewayManagerFromCurrentProvider_misconfiguredProvider_noGateways() throws IOException, NullPointerException { - Provider provider = getProvider(null, null, null, null, null, "ptdemo_misconfigured_gateway.json"); + Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_misconfigured_gateway.json", null); MockHelper.mockProviderObserver(provider); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); assertEquals(0, gatewaysManager.size()); @@ -115,7 +92,7 @@ public class GatewaysManagerTest { @Test public void testGatewayManagerFromCurrentProvider_threeGateways() { - Provider provider = getProvider(null, null, null,null, null, "ptdemo_three_mixed_gateways.json"); + Provider provider = getProvider(null, null, null, null,null, null, "ptdemo_three_mixed_gateways.json", null); MockHelper.mockProviderObserver(provider); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); assertEquals(3, gatewaysManager.size()); @@ -123,12 +100,10 @@ public class GatewaysManagerTest { @Test public void TestGetPosition_VpnProfileExtistingObfs4_returnPositionZero() throws JSONException, ConfigParser.ConfigParseError, IOException { - Provider provider = getProvider(null, null, null, null, null, "ptdemo_three_mixed_gateways.json"); + Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", null); JSONObject eipServiceJson = provider.getEipServiceJson(); JSONObject gateway1 = eipServiceJson.getJSONArray(GATEWAYS).getJSONObject(0); MockHelper.mockProviderObserver(provider); - mockStatic(PreferenceHelper.class); - when(PreferenceHelper.getUsePluggableTransports(any(Context.class))).thenReturn(true); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3); @@ -140,12 +115,10 @@ public class GatewaysManagerTest { @Test public void TestGetPosition_VpnProfileExtistingOpenvpn_returnPositionZero() throws JSONException, ConfigParser.ConfigParseError, IOException { - Provider provider = getProvider(null, null, null, null, null, "ptdemo_three_mixed_gateways.json"); + Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", null); JSONObject eipServiceJson = provider.getEipServiceJson(); JSONObject gateway1 = eipServiceJson.getJSONArray(GATEWAYS).getJSONObject(0); MockHelper.mockProviderObserver(provider); - mockStatic(PreferenceHelper.class); - when(PreferenceHelper.getUsePluggableTransports(any(Context.class))).thenReturn(false); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3); @@ -156,13 +129,41 @@ public class GatewaysManagerTest { } @Test + public void TestGetPosition_VpnProfileExistingObfs4FromPresortedList_returnsPositionOne() throws JSONException, ConfigParser.ConfigParseError, IOException { + Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", "ptdemo_three_mixed_gateways.geoip.json"); + JSONObject eipServiceJson = provider.getEipServiceJson(); + JSONObject gateway1 = eipServiceJson.getJSONArray(GATEWAYS).getJSONObject(0); + MockHelper.mockProviderObserver(provider); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + + VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3); + VpnProfile profile = configGenerator.createProfile(OBFS4); + profile.mGatewayIp = "37.218.247.60"; + + assertEquals(1, gatewaysManager.getPosition(profile)); + } + + @Test + public void TestGetPosition_VpnProfileExistingOpenvpnFromPresortedList_returnsPositionOne() throws JSONException, ConfigParser.ConfigParseError, IOException { + Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", "ptdemo_three_mixed_gateways.geoip.json"); + JSONObject eipServiceJson = provider.getEipServiceJson(); + JSONObject gateway1 = eipServiceJson.getJSONArray(GATEWAYS).getJSONObject(0); + MockHelper.mockProviderObserver(provider); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + + VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3); + VpnProfile profile = configGenerator.createProfile(OPENVPN); + profile.mGatewayIp = "37.218.247.60"; + + assertEquals(2, gatewaysManager.getPosition(profile)); + } + + @Test public void TestGetPosition_VpnProfileDifferentIp_returnMinusOne() throws JSONException, ConfigParser.ConfigParseError, IOException { - Provider provider = getProvider(null, null, null, null, null, "ptdemo_three_mixed_gateways.json"); + Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", null); JSONObject eipServiceJson = provider.getEipServiceJson(); JSONObject gateway1 = eipServiceJson.getJSONArray(GATEWAYS).getJSONObject(0); MockHelper.mockProviderObserver(provider); - mockStatic(PreferenceHelper.class); - when(PreferenceHelper.getUsePluggableTransports(any(Context.class))).thenReturn(true); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3); @@ -174,12 +175,10 @@ public class GatewaysManagerTest { @Test public void TestGetPosition_VpnProfileMoscow_returnOne() throws JSONException, ConfigParser.ConfigParseError, IOException { - Provider provider = getProvider(null, null, null, null, null, "ptdemo_three_mixed_gateways.json"); + Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", null); JSONObject eipServiceJson = provider.getEipServiceJson(); JSONObject gateway1 = eipServiceJson.getJSONArray(GATEWAYS).getJSONObject(1); MockHelper.mockProviderObserver(provider); - mockStatic(PreferenceHelper.class); - when(PreferenceHelper.getUsePluggableTransports(any(Context.class))).thenReturn(true); GatewaysManager gatewaysManager = new GatewaysManager(mockContext); VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3); @@ -190,10 +189,8 @@ public class GatewaysManagerTest { } @Test - public void TestSelectN_selectFirstObfs4Connection_returnThirdGateway() throws JSONException, ConfigParser.ConfigParseError, IOException { - Provider provider = getProvider(null, null, null, null, null, "ptdemo_two_openvpn_one_pt_gateways.json"); - JSONObject eipServiceJson = provider.getEipServiceJson(); - JSONObject gateway3 = eipServiceJson.getJSONArray(GATEWAYS).getJSONObject(2); + public void TestSelectN_selectFirstObfs4Connection_returnThirdGateway() { + Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_two_openvpn_one_pt_gateways.json", null); MockHelper.mockProviderObserver(provider); mockStatic(PreferenceHelper.class); @@ -203,6 +200,37 @@ public class GatewaysManagerTest { assertEquals("37.12.247.10", gatewaysManager.select(0).getRemoteIP()); } + @Test + public void testSelectN_selectFromPresortedGateways_returnsGatewaysInPresortedOrder() { + Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", "ptdemo_three_mixed_gateways.geoip.json"); + + MockHelper.mockProviderObserver(provider); + //use openvpn, not pluggable transports + mockStatic(PreferenceHelper.class); + when(PreferenceHelper.getUsePluggableTransports(any(Context.class))).thenReturn(false); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + + assertEquals("manila.bitmask.net", gatewaysManager.select(0).getHost()); + assertEquals("moscow.bitmask.net", gatewaysManager.select(1).getHost()); + assertEquals("pt.demo.bitmask.net", gatewaysManager.select(2).getHost()); + } + + @Test + public void testSelectN_selectObfs4FromPresortedGateways_returnsObfs4GatewaysInPresortedOrder() { + Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", "ptdemo_three_mixed_gateways.geoip.json"); + + MockHelper.mockProviderObserver(provider); + //use openvpn, not pluggable transports + mockStatic(PreferenceHelper.class); + when(PreferenceHelper.getUsePluggableTransports(any(Context.class))).thenReturn(true); + GatewaysManager gatewaysManager = new GatewaysManager(mockContext); + + assertEquals("moscow.bitmask.net", gatewaysManager.select(0).getHost()); + assertEquals("pt.demo.bitmask.net", gatewaysManager.select(1).getHost()); + assertNull(gatewaysManager.select(2)); + } + + private String getJsonStringFor(String filename) throws IOException { return TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream(filename)); } diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java index 753a9474..efa6f78c 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java @@ -25,6 +25,7 @@ import android.os.Bundle; import android.text.TextUtils; import org.json.JSONException; +import org.json.JSONObject; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -44,18 +45,24 @@ import se.leap.bitmaskclient.ProviderAPI; import se.leap.bitmaskclient.ProviderApiConnector; import se.leap.bitmaskclient.ProviderApiManager; import se.leap.bitmaskclient.ProviderApiManagerBase; +import se.leap.bitmaskclient.testutils.BackendMockResponses.GeoIpServiceIsDownBackendResponse; import se.leap.bitmaskclient.testutils.MockSharedPreferences; import se.leap.bitmaskclient.utils.ConfigHelper; import se.leap.bitmaskclient.utils.PreferenceHelper; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; +import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_GEOIP_JSON; import static se.leap.bitmaskclient.ProviderAPI.ERRORS; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_GEOIP_JSON; +import static se.leap.bitmaskclient.ProviderAPI.PARAMETERS; import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_NOK; import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_OK; import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_CASE_FETCH_EIP_SERVICE_CERTIFICATE_INVALID; import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_CASE_MICONFIGURED_PROVIDER; import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_CASE_UPDATED_CERTIFICATE; +import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_GEOIP_SERVICE_IS_DOWN; import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.NO_ERROR; import static se.leap.bitmaskclient.testutils.MockHelper.mockBundle; import static se.leap.bitmaskclient.testutils.MockHelper.mockClientGenerator; @@ -155,7 +162,7 @@ public class ProviderApiManagerTest { @Test public void test_handleIntentSetupProvider_happyPath_no_preseededProviderAndCA() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { - Provider provider = new Provider("https://riseup.net"); + Provider provider = getConfiguredProvider(); mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); mockProviderApiConnector(NO_ERROR); @@ -269,7 +276,7 @@ public class ProviderApiManagerTest { @Test public void test_handleIntentSetupProvider_preseededProviderAndCA_outdatedCertificate() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { - Provider provider = getProvider(null ,null, null, "outdated_cert.pem", null, null); + Provider provider = getProvider(null ,null, null, null, "outdated_cert.pem", null, null, null); mockProviderApiConnector(NO_ERROR); providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); @@ -290,7 +297,7 @@ public class ProviderApiManagerTest { @Test public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_outdatedCertificate() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { - Provider provider = new Provider("https://riseup.net"); + Provider provider = getConfiguredProvider(); //new Provider("https://riseup.net"); mockProviderApiConnector(NO_ERROR); mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply(); mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("outdated_cert.pem"))).apply(); @@ -386,7 +393,7 @@ public class ProviderApiManagerTest { @Test public void test_handleIntentSetupProvider_outdatedPreseededProviderAndCA_successfulConfiguration() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException { - Provider provider = getProvider(null, null, null, null, "riseup_net_outdated_config.json", null); + Provider provider = getProvider(null, null, null, null, null, "riseup_net_outdated_config.json", null, null); mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); mockProviderApiConnector(NO_ERROR); @@ -431,4 +438,126 @@ public class ProviderApiManagerTest { providerApiManager.handleIntent(providerApiCommand); } + + + @Test + public void test_handleIntentGetGeoip_happyPath() throws IOException, NoSuchAlgorithmException, CertificateEncodingException, JSONException { + if ("insecure".equals(BuildConfig.FLAVOR_implementation )) { + return; + } + + Provider inputProvider = getConfiguredProvider(); + inputProvider.setGeoIpJson(new JSONObject()); + Provider expectedProvider = getConfiguredProvider(); + mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); + mockProviderApiConnector(NO_ERROR); + providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); + + Bundle expectedResult = mockBundle(); + expectedResult.putBoolean(EIP_ACTION_START, true); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, true); + expectedResult.putParcelable(PROVIDER_KEY, expectedProvider); + + Intent providerApiCommand = mockIntent(); + + providerApiCommand.setAction(ProviderAPI.DOWNLOAD_GEOIP_JSON); + Bundle extrasBundle = mockBundle(); + extrasBundle.putBoolean(EIP_ACTION_START, true); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(CORRECTLY_DOWNLOADED_GEOIP_JSON, expectedResult)); + providerApiCommand.putExtra(PROVIDER_KEY, inputProvider); + providerApiCommand.putExtra(PARAMETERS, extrasBundle); + + providerApiManager.handleIntent(providerApiCommand); + + } + + + @Test + public void test_handleIntentGetGeoip_serviceDown_failToDownload() throws IOException, NoSuchAlgorithmException, CertificateEncodingException, JSONException { + if ("insecure".equals(BuildConfig.FLAVOR_implementation)) { + return; + } + + Provider provider = getConfiguredProvider(); + mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); + mockProviderApiConnector(ERROR_GEOIP_SERVICE_IS_DOWN); + providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); + + Bundle expectedResult = mockBundle(); + expectedResult.putBoolean(EIP_ACTION_START, true); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, false); + expectedResult.putParcelable(PROVIDER_KEY, provider); + + Intent providerApiCommand = mockIntent(); + + providerApiCommand.setAction(ProviderAPI.DOWNLOAD_GEOIP_JSON); + Bundle extrasBundle = mockBundle(); + extrasBundle.putBoolean(EIP_ACTION_START, true); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(INCORRECTLY_DOWNLOADED_GEOIP_JSON, expectedResult)); + providerApiCommand.putExtra(PROVIDER_KEY, provider); + providerApiCommand.putExtra(PARAMETERS, extrasBundle); + + providerApiManager.handleIntent(providerApiCommand); + + } + + @Test + public void test_handleIntentGetGeoip_didNotReachTimeoutToFetchNew_returnsFailure() throws IOException, NoSuchAlgorithmException, CertificateEncodingException, JSONException { + if ("insecure".equals(BuildConfig.FLAVOR_implementation)) { + return; + } + + Provider provider = getConfiguredProvider(); + provider.setLastGeoIpUpdate(System.currentTimeMillis()); + mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); + mockProviderApiConnector(NO_ERROR); + providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); + + Bundle expectedResult = mockBundle(); + expectedResult.putBoolean(EIP_ACTION_START, true); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, false); + expectedResult.putParcelable(PROVIDER_KEY, provider); + + Intent providerApiCommand = mockIntent(); + + providerApiCommand.setAction(ProviderAPI.DOWNLOAD_GEOIP_JSON); + Bundle extrasBundle = mockBundle(); + extrasBundle.putBoolean(EIP_ACTION_START, true); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(INCORRECTLY_DOWNLOADED_GEOIP_JSON, expectedResult)); + providerApiCommand.putExtra(PROVIDER_KEY, provider); + providerApiCommand.putExtra(PARAMETERS, extrasBundle); + + providerApiManager.handleIntent(providerApiCommand); + } + + @Test + public void test_handleIntentGetGeoip_noGeoipServiceURLDefined_returnsFailure() throws IOException, NoSuchAlgorithmException, CertificateEncodingException, JSONException { + if ("insecure".equals(BuildConfig.FLAVOR_implementation)) { + return; + } + + Provider provider = getConfiguredProvider(); + provider.setGeoipUrl(null); + provider.setGeoIpJson(new JSONObject()); + mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); + mockProviderApiConnector(NO_ERROR); + providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); + + Bundle expectedResult = mockBundle(); + expectedResult.putBoolean(EIP_ACTION_START, true); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, false); + expectedResult.putParcelable(PROVIDER_KEY, provider); + + Intent providerApiCommand = mockIntent(); + + providerApiCommand.setAction(ProviderAPI.DOWNLOAD_GEOIP_JSON); + Bundle extrasBundle = mockBundle(); + extrasBundle.putBoolean(EIP_ACTION_START, true); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(INCORRECTLY_DOWNLOADED_GEOIP_JSON, expectedResult)); + providerApiCommand.putExtra(PROVIDER_KEY, provider); + providerApiCommand.putExtra(PARAMETERS, extrasBundle); + + providerApiManager.handleIntent(providerApiCommand); + } + } diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java index a10b1414..3e8dfd5f 100644 --- a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java @@ -32,6 +32,7 @@ public class BackendMockProvider { ERROR_CASE_UPDATED_CERTIFICATE, ERROR_CASE_MICONFIGURED_PROVIDER, ERROR_CASE_FETCH_EIP_SERVICE_CERTIFICATE_INVALID, + ERROR_GEOIP_SERVICE_IS_DOWN, ERROR_NO_RESPONSE_BODY, // => NullPointerException ERROR_DNS_RESOLUTION_ERROR, // => UnkownHostException ERROR_SOCKET_TIMEOUT, // => SocketTimeoutException @@ -62,6 +63,10 @@ public class BackendMockProvider { break; case ERROR_CASE_FETCH_EIP_SERVICE_CERTIFICATE_INVALID: new EipSerivceJsonInvalidCertificateBackendResponse(); + break; + case ERROR_GEOIP_SERVICE_IS_DOWN: + new GeoIpServiceIsDownBackendResponse(); + break; case ERROR_NO_RESPONSE_BODY: break; case ERROR_DNS_RESOLUTION_ERROR: diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/EipSerivceJsonInvalidCertificateBackendResponse.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/EipSerivceJsonInvalidCertificateBackendResponse.java index b84c5508..7c2d49cc 100644 --- a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/EipSerivceJsonInvalidCertificateBackendResponse.java +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/EipSerivceJsonInvalidCertificateBackendResponse.java @@ -52,6 +52,9 @@ public class EipSerivceJsonInvalidCertificateBackendResponse extends BaseBackend } else if (url.contains("config/eip-service.json")) { // download provider service json containing gateways, locations and openvpn settings throw new SSLHandshakeException("Invalid provider CA certificate"); + } else if (url.contains(":9001/json")) { + // download geoip json, containing a sorted list of gateways + return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.geoip.json")); } else if (url.contains("/users.json")) { //create new user //TODO: implement me diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/GeoIpServiceIsDownBackendResponse.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/GeoIpServiceIsDownBackendResponse.java new file mode 100644 index 00000000..3a08d8da --- /dev/null +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/GeoIpServiceIsDownBackendResponse.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2018 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.testutils.BackendMockResponses; + +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.IOException; +import java.net.ConnectException; + +import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString; + +/** + * Created by cyberta on 10.01.18. + */ + +public class GeoIpServiceIsDownBackendResponse extends BaseBackendResponse { + public GeoIpServiceIsDownBackendResponse() throws IOException { + super(); + } + + @Override + public Answer<String> getAnswerForRequestStringFromServer() { + return new Answer<String>() { + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + String url = (String) invocation.getArguments()[0]; + String requestMethod = (String) invocation.getArguments()[1]; + String jsonPayload = (String) invocation.getArguments()[2]; + + if (url.contains("/provider.json")) { + //download provider json + return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json")); + } else if (url.contains("/ca.crt")) { + //download provider ca cert + return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem")); + } else if (url.contains("config/eip-service.json")) { + // download provider service json containing gateways, locations and openvpn settings + return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.service.json")); + } else if (url.contains(":9001/json")) { + // download geoip json, containing a sorted list of gateways + throw new ConnectException("Failed to connect to api.black.riseup.net/198.252.153.107:9001"); + } else if (url.contains("/users.json")) { + //create new user + //TODO: implement me + } else if (url.contains("/sessions.json")) { + //srp auth: sendAToSRPServer + //TODO: implement me + } else if (url.contains("/sessions/parmegvtest10.json")){ + //srp auth: sendM1ToSRPServer + //TODO: implement me + } + + return null; + } + }; + } + + @Override + public Answer<Boolean> getAnswerForCanConnect() { + return new Answer<Boolean>() { + @Override + public Boolean answer(InvocationOnMock invocation) throws Throwable { + return true; + } + }; + } + + @Override + public Answer<Boolean> getAnswerForDelete() { + return new Answer<Boolean>() { + @Override + public Boolean answer(InvocationOnMock invocation) throws Throwable { + return true; + } + }; + } + +} diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/MisconfiguredProviderBackendResponse.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/MisconfiguredProviderBackendResponse.java index 4600e879..10e69bc3 100644 --- a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/MisconfiguredProviderBackendResponse.java +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/MisconfiguredProviderBackendResponse.java @@ -50,6 +50,9 @@ public class MisconfiguredProviderBackendResponse extends BaseBackendResponse { } else if (url.contains("config/eip-service.json")) { // download provider service json containing gateways, locations and openvpn settings return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.service.json")); + } else if (url.contains(":9001/json")) { + // download geoip json, containing a sorted list of gateways + return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.geoip.json")); } else if (url.contains("/users.json")) { //create new user //TODO: implement me diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/NoErrorBackendResponse.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/NoErrorBackendResponse.java index fa318e42..3c3a8ffa 100644 --- a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/NoErrorBackendResponse.java +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/NoErrorBackendResponse.java @@ -50,6 +50,9 @@ public class NoErrorBackendResponse extends BaseBackendResponse { } else if (url.contains("config/eip-service.json")) { // download provider service json containing gateways, locations and openvpn settings return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.service.json")); + } else if (url.contains(":9001/json")) { + // download geoip json, containing a sorted list of gateways + return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.geoip.json")); } else if (url.contains("/users.json")) { //create new user //TODO: implement me diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java index 1c205c83..d3d07308 100644 --- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java @@ -401,6 +401,8 @@ public class MockHelper { return providerFromPrefs.getCaCertFingerprint(); case Provider.CA_CERT: return providerFromPrefs.getCaCert(); + case Provider.GEOIP_URL: + return providerFromPrefs.getGeoipUrl().toString(); } return null; diff --git a/app/src/test/resources/ptdemo_three_mixed_gateways.geoip.json b/app/src/test/resources/ptdemo_three_mixed_gateways.geoip.json new file mode 100644 index 00000000..a72a85f5 --- /dev/null +++ b/app/src/test/resources/ptdemo_three_mixed_gateways.geoip.json @@ -0,0 +1,12 @@ +{ + "ip":"51.158.144.32", + "cc":"FR", + "city":"Paris", + "lat":48.8628, + "lon":2.3292, + "gateways":[ + "manila.bitmask.net", + "moscow.bitmask.net", + "pt.demo.bitmask.net" + ] +}
\ No newline at end of file diff --git a/app/src/test/resources/riseup.geoip.json b/app/src/test/resources/riseup.geoip.json new file mode 100644 index 00000000..b646052e --- /dev/null +++ b/app/src/test/resources/riseup.geoip.json @@ -0,0 +1,20 @@ +{ + "ip":"51.158.144.32", + "cc":"FR", + "city":"Paris", + "lat":48.8628, + "lon":2.3292, + "gateways":[ + "mouette.riseup.net", + "hoatzin.riseup.net", + "zarapito.riseup.net", + "redshank.riseup.net", + "shag.riseup.net", + "yal.riseup.net", + "gaei.riseup.net", + "cisne.riseup.net", + "swan.riseup.net", + "garza.riseup.net", + "gaviota.riseup.net" + ] +}
\ No newline at end of file |