From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Tad Date: Thu, 20 Sep 2018 21:44:53 -0400 Subject: [PATCH] Add support for routing over Tor Change-Id: Ibfe080c3d801af34fb64fda1b6b8f4f39a2b1ccf --- AndroidManifest.xml | 4 ++ res/layout/preferences_dialog.xml | 8 +++ res/values/strings.xml | 2 + .../lineageos/updater/UpdatesActivity.java | 12 ++++ .../updater/UpdatesCheckReceiver.java | 4 ++ .../updater/controller/UpdaterController.java | 8 +++ .../updater/download/DownloadClient.java | 8 ++- .../download/HttpURLConnectionClient.java | 27 ++++++-- src/org/lineageos/updater/misc/Constants.java | 1 + src/org/lineageos/updater/misc/Utils.java | 65 +++++++++++++++++++ 10 files changed, 134 insertions(+), 5 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 693ded4..91e85a3 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -12,6 +12,10 @@ + + + + + + Once a week Once a month Never + Perform requests over Tor Delete updates when installed Delete Copy URL @@ -87,6 +88,7 @@ The download failed. Please check your internet connection and try again later. The update verification failed. Download completed. + Orbot is not installed, disabling Tor routing! This update can\'t be installed on top of the current build. diff --git a/src/org/lineageos/updater/UpdatesActivity.java b/src/org/lineageos/updater/UpdatesActivity.java index 9c56f0b..766fb83 100644 --- a/src/org/lineageos/updater/UpdatesActivity.java +++ b/src/org/lineageos/updater/UpdatesActivity.java @@ -377,10 +377,14 @@ public class UpdatesActivity extends UpdatesListActivity { final DownloadClient downloadClient; try { + if(Utils.isOnionRoutingEnabled(getApplicationContext())) { + Utils.requestStartOrbot(getApplicationContext()); + } downloadClient = new DownloadClient.Builder() .setUrl(url) .setDestination(jsonFileTmp) .setDownloadCallback(callback) + .setUseOnionRouting(Utils.isOnionRoutingEnabled(getApplicationContext())) .build(); } catch (IOException exception) { Log.e(TAG, "Could not build download client"); @@ -480,6 +484,7 @@ public class UpdatesActivity extends UpdatesListActivity { private void showPreferencesDialog() { View view = LayoutInflater.from(this).inflate(R.layout.preferences_dialog, null); Spinner autoCheckInterval = view.findViewById(R.id.preferences_auto_updates_check_interval); + SwitchCompat onionRouting = view.findViewById(R.id.preferences_onion_routing); SwitchCompat autoDelete = view.findViewById(R.id.preferences_auto_delete_updates); SwitchCompat dataWarning = view.findViewById(R.id.preferences_mobile_data_warning); SwitchCompat abPerfMode = view.findViewById(R.id.preferences_ab_perf_mode); @@ -491,6 +496,7 @@ public class UpdatesActivity extends UpdatesListActivity { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); autoCheckInterval.setSelection(Utils.getUpdateCheckSetting(this)); + onionRouting.setChecked(prefs.getBoolean(Constants.PREF_ONION_ROUTING, false)); autoDelete.setChecked(prefs.getBoolean(Constants.PREF_AUTO_DELETE_UPDATES, false)); dataWarning.setChecked(prefs.getBoolean(Constants.PREF_MOBILE_DATA_WARNING, true)); abPerfMode.setChecked(prefs.getBoolean(Constants.PREF_AB_PERF_MODE, false)); @@ -530,6 +536,8 @@ public class UpdatesActivity extends UpdatesListActivity { prefs.edit() .putInt(Constants.PREF_AUTO_UPDATES_CHECK_INTERVAL, autoCheckInterval.getSelectedItemPosition()) + .putBoolean(Constants.PREF_ONION_ROUTING, + onionRouting.isChecked() && Utils.isOrbotInstalled(getApplicationContext())) .putBoolean(Constants.PREF_AUTO_DELETE_UPDATES, autoDelete.isChecked()) .putBoolean(Constants.PREF_MOBILE_DATA_WARNING, dataWarning.isChecked()) .putBoolean(Constants.PREF_AB_PERF_MODE, abPerfMode.isChecked()) @@ -542,6 +550,10 @@ public class UpdatesActivity extends UpdatesListActivity { UpdatesCheckReceiver.cancelUpdatesCheck(this); } + if(onionRouting.isChecked() && !Utils.isOrbotInstalled(getApplicationContext())) { + showSnackbar(R.string.snack_orbot_not_available, Snackbar.LENGTH_LONG); + } + if (Utils.isABDevice()) { boolean enableABPerfMode = abPerfMode.isChecked(); mUpdaterService.getUpdaterController().setPerformanceMode(enableABPerfMode); diff --git a/src/org/lineageos/updater/UpdatesCheckReceiver.java b/src/org/lineageos/updater/UpdatesCheckReceiver.java index 7fa9cd5..962fd00 100644 --- a/src/org/lineageos/updater/UpdatesCheckReceiver.java +++ b/src/org/lineageos/updater/UpdatesCheckReceiver.java @@ -110,10 +110,14 @@ public class UpdatesCheckReceiver extends BroadcastReceiver { }; try { + if(Utils.isOnionRoutingEnabled(context)) { + Utils.requestStartOrbot(context); + } DownloadClient downloadClient = new DownloadClient.Builder() .setUrl(url) .setDestination(jsonNew) .setDownloadCallback(callback) + .setUseOnionRouting(Utils.isOnionRoutingEnabled(context)) .build(); downloadClient.start(); } catch (IOException e) { diff --git a/src/org/lineageos/updater/controller/UpdaterController.java b/src/org/lineageos/updater/controller/UpdaterController.java index 5d7d51a..7ac5f5b 100644 --- a/src/org/lineageos/updater/controller/UpdaterController.java +++ b/src/org/lineageos/updater/controller/UpdaterController.java @@ -374,12 +374,16 @@ public class UpdaterController { update.setFile(destination); DownloadClient downloadClient; try { + if(Utils.isOnionRoutingEnabled(mContext)) { + Utils.requestStartOrbot(mContext); + } downloadClient = new DownloadClient.Builder() .setUrl(update.getDownloadUrl()) .setDestination(update.getFile()) .setDownloadCallback(getDownloadCallback(downloadId)) .setProgressListener(getProgressListener(downloadId)) .setUseDuplicateLinks(true) + .setUseOnionRouting(Utils.isOnionRoutingEnabled(mContext)) .build(); } catch (IOException exception) { Log.e(TAG, "Could not build download client"); @@ -419,6 +423,9 @@ public class UpdaterController { verifyUpdateAsync(downloadId); notifyUpdateChange(downloadId); } else { + if(Utils.isOnionRoutingEnabled(mContext)) { + Utils.requestStartOrbot(mContext); + } DownloadClient downloadClient; try { downloadClient = new DownloadClient.Builder() @@ -427,6 +434,7 @@ public class UpdaterController { .setDownloadCallback(getDownloadCallback(downloadId)) .setProgressListener(getProgressListener(downloadId)) .setUseDuplicateLinks(true) + .setUseOnionRouting(Utils.isOnionRoutingEnabled(mContext)) .build(); } catch (IOException exception) { Log.e(TAG, "Could not build download client"); diff --git a/src/org/lineageos/updater/download/DownloadClient.java b/src/org/lineageos/updater/download/DownloadClient.java index 3494947..7a7f7cf 100644 --- a/src/org/lineageos/updater/download/DownloadClient.java +++ b/src/org/lineageos/updater/download/DownloadClient.java @@ -60,6 +60,7 @@ public interface DownloadClient { private DownloadClient.DownloadCallback mCallback; private DownloadClient.ProgressListener mProgressListener; private boolean mUseDuplicateLinks; + private boolean mOnionRouting; public DownloadClient build() throws IOException { if (mUrl == null) { @@ -70,7 +71,7 @@ public interface DownloadClient { throw new IllegalStateException("No download callback defined"); } return new HttpURLConnectionClient(mUrl, mDestination, mProgressListener, mCallback, - mUseDuplicateLinks); + mUseDuplicateLinks, mOnionRouting); } public Builder setUrl(String url) { @@ -97,5 +98,10 @@ public interface DownloadClient { mUseDuplicateLinks = useDuplicateLinks; return this; } + + public Builder setUseOnionRouting(boolean onionRouting) { + mOnionRouting = onionRouting; + return this; + } } } diff --git a/src/org/lineageos/updater/download/HttpURLConnectionClient.java b/src/org/lineageos/updater/download/HttpURLConnectionClient.java index b9c4b5d..b94fff0 100644 --- a/src/org/lineageos/updater/download/HttpURLConnectionClient.java +++ b/src/org/lineageos/updater/download/HttpURLConnectionClient.java @@ -18,12 +18,16 @@ package org.lineageos.updater.download; import android.os.SystemClock; import android.util.Log; +import org.lineageos.updater.misc.Utils; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.Proxy; import java.net.URL; import java.util.Comparator; import java.util.List; @@ -42,6 +46,7 @@ public class HttpURLConnectionClient implements DownloadClient { private final DownloadClient.ProgressListener mProgressListener; private final DownloadClient.DownloadCallback mCallback; private final boolean mUseDuplicateLinks; + private final boolean mUseOnionRouting; private DownloadThread mDownloadThread; @@ -56,8 +61,14 @@ public class HttpURLConnectionClient implements DownloadClient { HttpURLConnectionClient(String url, File destination, DownloadClient.ProgressListener progressListener, DownloadClient.DownloadCallback callback, - boolean useDuplicateLinks) throws IOException { - mClient = (HttpURLConnection) new URL(url).openConnection(); + boolean useDuplicateLinks, boolean useOnionRouting) throws IOException { + mUseOnionRouting = useOnionRouting; + if(mUseOnionRouting) { + Proxy orbot = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", 9050)); + mClient = (HttpURLConnection) new URL(url).openConnection(orbot); + } else { + mClient = (HttpURLConnection) new URL(url).openConnection(); + } mDestination = destination; mProgressListener = progressListener; mCallback = callback; @@ -177,7 +188,12 @@ public class HttpURLConnectionClient implements DownloadClient { private void changeClientUrl(URL newUrl) throws IOException { String range = mClient.getRequestProperty("Range"); mClient.disconnect(); - mClient = (HttpURLConnection) newUrl.openConnection(); + if(mUseOnionRouting) { + Proxy orbot = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", 9050)); + mClient = (HttpURLConnection) newUrl.openConnection(orbot); + } else { + mClient = (HttpURLConnection) newUrl.openConnection(); + } if (range != null) { mClient.setRequestProperty("Range", range); } @@ -232,7 +248,7 @@ public class HttpURLConnectionClient implements DownloadClient { } Log.d(TAG, "Downloading from " + newUrl); changeClientUrl(url); - mClient.setConnectTimeout(5000); + mClient.setConnectTimeout(mUseOnionRouting ? 45000 : 5000); mClient.connect(); if (!isSuccessCode(mClient.getResponseCode())) { throw new IOException("Server replied with " + mClient.getResponseCode()); @@ -257,6 +273,9 @@ public class HttpURLConnectionClient implements DownloadClient { public void run() { boolean justResumed = false; try { + if(mUseOnionRouting) { + Utils.waitUntilOrbotIsAvailable(); + } mClient.setInstanceFollowRedirects(!mUseDuplicateLinks); mClient.connect(); int responseCode = mClient.getResponseCode(); diff --git a/src/org/lineageos/updater/misc/Constants.java b/src/org/lineageos/updater/misc/Constants.java index 6144ed7..37e81d8 100644 --- a/src/org/lineageos/updater/misc/Constants.java +++ b/src/org/lineageos/updater/misc/Constants.java @@ -30,6 +30,7 @@ public final class Constants { public static final String PREF_LAST_UPDATE_CHECK = "last_update_check"; public static final String PREF_AUTO_UPDATES_CHECK_INTERVAL = "auto_updates_check_interval"; + public static final String PREF_ONION_ROUTING = "onion_routing"; public static final String PREF_AUTO_DELETE_UPDATES = "auto_delete_updates"; public static final String PREF_AB_PERF_MODE = "ab_perf_mode"; public static final String PREF_MOBILE_DATA_WARNING = "pref_mobile_data_warning"; diff --git a/src/org/lineageos/updater/misc/Utils.java b/src/org/lineageos/updater/misc/Utils.java index 69a5252..2b42726 100644 --- a/src/org/lineageos/updater/misc/Utils.java +++ b/src/org/lineageos/updater/misc/Utils.java @@ -44,6 +44,7 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; +import java.net.Socket; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; @@ -149,11 +150,75 @@ public class Utils { return updates; } + //Credit: https://stackoverflow.com/a/6758962 + public static boolean isPackageInstalled(Context context, String packageID) { + PackageManager pm = context.getPackageManager(); + try { + pm.getPackageInfo(packageID, PackageManager.GET_META_DATA); + } catch(PackageManager.NameNotFoundException e) { + return false; + } + return true; + } + + public static boolean isOrbotInstalled(Context context) { + return isPackageInstalled(context, "org.torproject.android"); + } + + public static boolean isOnionRoutingEnabled(Context context) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + return preferences.getBoolean(Constants.PREF_ONION_ROUTING, false); + } + + //Credit: OrbotHelper/NetCipher + public static void requestStartOrbot(Context context) { + Intent intent = new Intent("org.torproject.android.intent.action.START"); + intent.setPackage("org.torproject.android"); + intent.putExtra("org.torproject.android.intent.extra.PACKAGE_NAME", context.getPackageName()); + context.sendBroadcast(intent); + } + + //Credit: https://www.geekality.net/2013/04/30/java-simple-check-to-see-if-a-server-is-listening-on-a-port/ + public static boolean isPortListening(String host, int port) { + Socket s = null; + try { + s = new Socket(host, port); + return true; + } catch(Exception e) { + return false; + } finally { + if (s != null) { + try { + s.close(); + } catch(Exception e1) { + } + } + } + } + + public static boolean waitUntilOrbotIsAvailable() { + int tries = 0; + boolean listening; + while(!(listening = isPortListening("127.0.0.1", 9050)) && tries <= 60) { + tries++; + try { + Thread.sleep(1000); + } catch(Exception e) { + + } + } + return listening; + } + public static String getServerURL(Context context) { String incrementalVersion = SystemProperties.get(Constants.PROP_BUILD_VERSION_INCREMENTAL); String device = SystemProperties.get(Constants.PROP_NEXT_DEVICE, SystemProperties.get(Constants.PROP_DEVICE)); String server = "0OTA_SERVER_CLEARNET0"; + String serverOnion = "0OTA_SERVER_ONION0"; + if(serverOnion.toLowerCase().startsWith("http") && isOnionRoutingEnabled(context)) { + server = serverOnion; + } return server + "?base=LineageOS&device=" + device + "&inc=" + incrementalVersion; }