Merge branch 'smooth-tor' into 'master'
Smooth Tor setup This is a big reworking of the Tor support to make it really easy to setup, and to make .onion addresses work automatically even when "Use Tor" is off. I needed to make the NetCipher v1.2.1 release to support this, hence it was originally WIP:. See merge request !216
This commit is contained in:
commit
4ec9cee143
@ -1,3 +1,8 @@
|
||||
|
||||
* add simple "Use Tor" preference
|
||||
|
||||
* Enable TLS v1.2 for officials repo on all devices that support it
|
||||
|
||||
### 0.98.1 (2016-02-14)
|
||||
|
||||
* Fix crash when entering only a space into the search dialog
|
||||
|
@ -17,6 +17,7 @@ dependencies {
|
||||
compile 'com.google.zxing:core:3.2.1'
|
||||
compile 'eu.chainfire:libsuperuser:1.0.0.201602011018'
|
||||
compile 'cc.mvdan.accesspoint:library:0.1.3'
|
||||
compile 'info.guardianproject.netcipher:netcipher:1.2.1'
|
||||
compile 'commons-net:commons-net:3.4'
|
||||
compile 'org.openhab.jmdns:jmdns:3.4.2'
|
||||
compile('ch.acra:acra:4.8.2') {
|
||||
@ -71,6 +72,7 @@ if (!hasProperty('sourceDeps')) {
|
||||
'eu.chainfire:libsuperuser:952c5fc82f9c31d31d2b6a7054ee267dac1685fb037a254888c73c48de661eaf',
|
||||
'cc.mvdan.accesspoint:library:dc89a085d6bc40381078b8dd7776b12bde0dbaf8ffbcddb17ec4ebc3edecc7ba',
|
||||
'commons-net:commons-net:38cf2eca826b8bcdb236fc1f2e79e0c6dd8e7e0f5c44a3b8e839a1065b2fbe2e',
|
||||
'info.guardianproject.netcipher:netcipher:611ec5bde9d799fd57e1efec5c375f9f460de2cdda98918541decc9a7d02f2ad',
|
||||
'org.openhab.jmdns:jmdns:7a4b34b5606bbd2aff7fdfe629edcb0416fccd367fb59a099f210b9aba4f0bce',
|
||||
'com.madgag.spongycastle:pkix:6aba9b2210907a3d46dd3dcac782bb3424185290468d102d5207ebdc9796a905',
|
||||
'com.madgag.spongycastle:prov:029f26cd6b67c06ffa05702d426d472c141789001bcb15b7262ed86c868e5643',
|
||||
|
4
F-Droid/proguard-rules.pro
vendored
4
F-Droid/proguard-rules.pro
vendored
@ -10,6 +10,10 @@
|
||||
-dontnote android.support.**
|
||||
-dontnote **ILicensingService
|
||||
|
||||
# StrongHttpsClient and its support classes are totally unused, so the
|
||||
# ch.boye.httpclientandroidlib.** classes are also unneeded
|
||||
-dontwarn info.guardianproject.netcipher.client.**
|
||||
|
||||
# These libraries are known to break if minification is enabled on them. They
|
||||
# use reflection to instantiate classes, for example. If the keep flags are
|
||||
# removed, proguard will strip classes which are required, which may result in
|
||||
|
@ -166,6 +166,8 @@
|
||||
<string name="next">Next</string>
|
||||
<string name="skip">Skip</string>
|
||||
|
||||
<string name="useTor">Use Tor</string>
|
||||
<string name="useTorSummary">Force download traffic through Tor for increased privacy</string>
|
||||
<string name="proxy">Proxy</string>
|
||||
<string name="enable_proxy_title">Enable HTTP Proxy</string>
|
||||
<string name="enable_proxy_summary">Configure HTTP Proxy for all network requests</string>
|
||||
|
@ -48,6 +48,10 @@
|
||||
android:title="@string/local_repo_name" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/proxy" >
|
||||
<CheckBoxPreference
|
||||
android:key="useTor"
|
||||
android:summary="@string/useTorSummary"
|
||||
android:title="@string/useTor" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="enableProxy"
|
||||
|
@ -28,6 +28,7 @@ import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@ -137,6 +138,7 @@ public class FDroid extends AppCompatActivity implements SearchView.OnQueryTextL
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
FDroidApp.checkStartTor(this);
|
||||
// AppDetails and RepoDetailsActivity set different NFC actions, so reset here
|
||||
NfcHelper.setAndroidBeam(this, getApplication().getPackageName());
|
||||
checkForAddRepoIntent(getIntent());
|
||||
@ -289,6 +291,10 @@ public class FDroid extends AppCompatActivity implements SearchView.OnQueryTextL
|
||||
MenuItem btItem = menu.findItem(R.id.action_bluetooth_apk);
|
||||
btItem.setVisible(false);
|
||||
}
|
||||
if (Build.VERSION.SDK_INT < 10) {
|
||||
MenuItem menuItem = menu.findItem(R.id.action_swap);
|
||||
menuItem.setVisible(false);
|
||||
}
|
||||
|
||||
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
|
||||
searchMenuItem = menu.findItem(R.id.action_search);
|
||||
|
@ -63,6 +63,8 @@ import java.net.URLStreamHandlerFactory;
|
||||
import java.security.Security;
|
||||
import java.util.Locale;
|
||||
|
||||
import info.guardianproject.netcipher.NetCipher;
|
||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||
import sun.net.www.protocol.bluetooth.Handler;
|
||||
|
||||
@ReportsCrashes(mailTo = "reports@f-droid.org",
|
||||
@ -296,6 +298,8 @@ public class FDroidApp extends Application {
|
||||
startService(new Intent(FDroidApp.this, WifiStateChangeService.class));
|
||||
}
|
||||
});
|
||||
|
||||
configureTor(Preferences.get().isTorEnabled());
|
||||
}
|
||||
|
||||
@TargetApi(18)
|
||||
@ -352,4 +356,28 @@ public class FDroidApp extends Application {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean useTor;
|
||||
|
||||
/**
|
||||
* Set the proxy settings based on whether Tor should be enabled or not.
|
||||
*/
|
||||
public static void configureTor(boolean enabled) {
|
||||
useTor = enabled;
|
||||
if (useTor) {
|
||||
NetCipher.useTor();
|
||||
} else {
|
||||
NetCipher.clearProxy();
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkStartTor(Context context) {
|
||||
if (useTor) {
|
||||
OrbotHelper.requestStartTor(context);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isUsingTor() {
|
||||
return useTor;
|
||||
}
|
||||
}
|
||||
|
@ -25,9 +25,11 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
|
||||
|
||||
private static final String TAG = "Preferences";
|
||||
|
||||
private final Context context;
|
||||
private final SharedPreferences preferences;
|
||||
|
||||
private Preferences(Context context) {
|
||||
this.context = context;
|
||||
preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
preferences.registerOnSharedPreferenceChangeListener(this);
|
||||
if (preferences.getString(PREF_LOCAL_REPO_NAME, null) == null) {
|
||||
@ -54,6 +56,7 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
|
||||
public static final String PREF_LOCAL_REPO_NAME = "localRepoName";
|
||||
public static final String PREF_LOCAL_REPO_HTTPS = "localRepoHttps";
|
||||
public static final String PREF_LANGUAGE = "language";
|
||||
public static final String PREF_USE_TOR = "useTor";
|
||||
public static final String PREF_ENABLE_PROXY = "enableProxy";
|
||||
public static final String PREF_PROXY_HOST = "proxyHost";
|
||||
public static final String PREF_PROXY_PORT = "proxyPort";
|
||||
@ -161,6 +164,16 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
|
||||
return preferences.getString(PREF_LOCAL_REPO_NAME, getDefaultLocalRepoName());
|
||||
}
|
||||
|
||||
/**
|
||||
* This preference's default is set dynamically based on whether Orbot is
|
||||
* installed. If Orbot is installed, default to using Tor, the user can still override
|
||||
*/
|
||||
public boolean isTorEnabled() {
|
||||
// TODO enable once Orbot can auto-start after first install
|
||||
//return preferences.getBoolean(PREF_USE_TOR, OrbotHelper.requestStartTor(context));
|
||||
return preferences.getBoolean(PREF_USE_TOR, false);
|
||||
}
|
||||
|
||||
public boolean isProxyEnabled() {
|
||||
return preferences.getBoolean(PREF_ENABLE_PROXY, DEFAULT_ENABLE_PROXY);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package org.fdroid.fdroid.localrepo.type;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
|
||||
@ -70,12 +71,13 @@ public abstract class SwapType {
|
||||
}
|
||||
|
||||
public void startInBackground() {
|
||||
new Thread() {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
public void run() {
|
||||
SwapType.this.start();
|
||||
protected Void doInBackground(Void... params) {
|
||||
start();
|
||||
return null;
|
||||
}
|
||||
}.start();
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void ensureRunning() {
|
||||
@ -85,21 +87,23 @@ public abstract class SwapType {
|
||||
}
|
||||
|
||||
public void ensureRunningInBackground() {
|
||||
new Thread() {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
public void run() {
|
||||
protected Void doInBackground(Void... params) {
|
||||
ensureRunning();
|
||||
return null;
|
||||
}
|
||||
}.start();
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public void stopInBackground() {
|
||||
new Thread() {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
public void run() {
|
||||
SwapType.this.stop();
|
||||
protected Void doInBackground(Void... params) {
|
||||
stop();
|
||||
return null;
|
||||
}
|
||||
}.start();
|
||||
}.execute();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -55,9 +55,6 @@ public class DownloaderFactory {
|
||||
String macAddress = url.getHost().replace("-", ":");
|
||||
return new BluetoothDownloader(context, macAddress, url, destFile);
|
||||
}
|
||||
if (isOnionAddress(url)) {
|
||||
return new TorHttpDownloader(context, url, destFile);
|
||||
}
|
||||
if (isLocalFile(url)) {
|
||||
return new LocalFileDownloader(context, url, destFile);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.fdroid.fdroid.net;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
|
||||
@ -22,8 +23,11 @@ import java.net.Proxy;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import info.guardianproject.netcipher.NetCipher;
|
||||
|
||||
public class HttpDownloader extends Downloader {
|
||||
private static final String TAG = "HttpDownloader";
|
||||
|
||||
@ -32,7 +36,7 @@ public class HttpDownloader extends Downloader {
|
||||
|
||||
protected HttpURLConnection connection;
|
||||
private Credentials credentials;
|
||||
private int statusCode = -1;
|
||||
private int statusCode = -1;
|
||||
|
||||
HttpDownloader(Context context, URL url, File destFile)
|
||||
throws FileNotFoundException, MalformedURLException {
|
||||
@ -52,6 +56,7 @@ public class HttpDownloader extends Downloader {
|
||||
* checking out that follows redirects up to a certain point. I guess though the correct way
|
||||
* is probably to check for a loop (keep a list of all URLs redirected to and if you hit the
|
||||
* same one twice, bail with an exception).
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
@ -92,17 +97,29 @@ public class HttpDownloader extends Downloader {
|
||||
if (connection != null) {
|
||||
return;
|
||||
}
|
||||
Preferences prefs = Preferences.get();
|
||||
if (prefs.isProxyEnabled() && !isSwapUrl()) {
|
||||
SocketAddress sa = new InetSocketAddress(prefs.getProxyHost(), prefs.getProxyPort());
|
||||
Proxy proxy = new Proxy(Proxy.Type.HTTP, sa);
|
||||
connection = (HttpURLConnection) sourceUrl.openConnection(proxy);
|
||||
} else {
|
||||
|
||||
if (isSwapUrl()) {
|
||||
// swap never works with a proxy, its unrouted IP on the same subnet
|
||||
connection = (HttpURLConnection) sourceUrl.openConnection();
|
||||
if (credentials != null) {
|
||||
credentials.authenticate(connection);
|
||||
} else {
|
||||
Preferences prefs = Preferences.get();
|
||||
if (prefs.isProxyEnabled()) {
|
||||
// if "Use Tor" is set, NetCipher will ignore these proxy settings
|
||||
SocketAddress sa = new InetSocketAddress(prefs.getProxyHost(), prefs.getProxyPort());
|
||||
NetCipher.setProxy(new Proxy(Proxy.Type.HTTP, sa));
|
||||
}
|
||||
connection = NetCipher.getHttpURLConnection(sourceUrl);
|
||||
}
|
||||
|
||||
// workaround until NetCipher supports HTTPS SNI
|
||||
// https://gitlab.com/fdroid/fdroidclient/issues/431
|
||||
if (connection instanceof HttpsURLConnection
|
||||
&& !TextUtils.equals(sourceUrl.getHost(), "f-droid.org")
|
||||
&& !TextUtils.equals(sourceUrl.getHost(), "guardianproject.info")) {
|
||||
((HttpsURLConnection) connection).setSSLSocketFactory(HttpsURLConnection.getDefaultSSLSocketFactory());
|
||||
}
|
||||
|
||||
if (credentials != null) {
|
||||
credentials.authenticate(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,4 +176,4 @@ public class HttpDownloader extends Downloader {
|
||||
public void close() {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
package org.fdroid.fdroid.net;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Proxy;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URL;
|
||||
|
||||
public class TorHttpDownloader extends HttpDownloader {
|
||||
|
||||
TorHttpDownloader(Context context, URL url, File destFile)
|
||||
throws FileNotFoundException, MalformedURLException {
|
||||
super(context, url, destFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupConnection() throws IOException {
|
||||
SocketAddress sa = new InetSocketAddress("127.0.0.1", 8118);
|
||||
Proxy tor = new Proxy(Proxy.Type.HTTP, sa);
|
||||
connection = (HttpURLConnection) sourceUrl.openConnection(tor);
|
||||
}
|
||||
}
|
@ -87,12 +87,11 @@ public class WifiStateChangeService extends Service {
|
||||
wifiInfo = wifiManager.getConnectionInfo();
|
||||
FDroidApp.ipAddressString = formatIpAddress(wifiInfo.getIpAddress());
|
||||
DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
|
||||
if (dhcpInfo == null) {
|
||||
return null;
|
||||
}
|
||||
String netmask = formatIpAddress(dhcpInfo.netmask);
|
||||
if (!TextUtils.isEmpty(FDroidApp.ipAddressString) && netmask != null) {
|
||||
FDroidApp.subnetInfo = new SubnetUtils(FDroidApp.ipAddressString, netmask).getInfo();
|
||||
if (dhcpInfo != null) {
|
||||
String netmask = formatIpAddress(dhcpInfo.netmask);
|
||||
if (!TextUtils.isEmpty(FDroidApp.ipAddressString) && netmask != null) {
|
||||
FDroidApp.subnetInfo = new SubnetUtils(FDroidApp.ipAddressString, netmask).getInfo();
|
||||
}
|
||||
}
|
||||
} else if (wifiState == WifiManager.WIFI_STATE_DISABLED
|
||||
|| wifiState == WifiManager.WIFI_STATE_DISABLING) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.fdroid.fdroid.net.bluetooth;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
@ -21,6 +22,7 @@ public class BluetoothClient {
|
||||
device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress);
|
||||
}
|
||||
|
||||
@TargetApi(10)
|
||||
public BluetoothConnection openConnection() throws IOException {
|
||||
|
||||
BluetoothSocket socket = null;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.fdroid.fdroid.net.bluetooth;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothServerSocket;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
@ -61,6 +62,7 @@ public class BluetoothServer extends Thread {
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(10)
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
|
@ -127,6 +127,7 @@ public class ManageReposActivity extends ActionBarActivity {
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
FDroidApp.checkStartTor(this);
|
||||
|
||||
/* let's see if someone is trying to send us a new repo */
|
||||
addRepoFromIntent(getIntent());
|
||||
|
@ -21,6 +21,9 @@ import org.fdroid.fdroid.PreferencesActivity;
|
||||
import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.installer.PrivilegedInstaller;
|
||||
|
||||
import info.guardianproject.netcipher.NetCipher;
|
||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||
|
||||
public class PreferencesFragment extends PreferenceFragment
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
@ -43,10 +46,16 @@ public class PreferencesFragment extends PreferenceFragment
|
||||
Preferences.PREF_PROXY_PORT,
|
||||
};
|
||||
|
||||
private static final int REQUEST_INSTALL_ORBOT = 0x1234;
|
||||
private CheckBoxPreference enableProxyCheckPref;
|
||||
private CheckBoxPreference useTorCheckPref;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
useTorCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_USE_TOR);
|
||||
enableProxyCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_ENABLE_PROXY);
|
||||
}
|
||||
|
||||
private void checkSummary(String key, int resId) {
|
||||
@ -279,6 +288,29 @@ public class PreferencesFragment extends PreferenceFragment
|
||||
|
||||
initPrivilegedInstallerPreference();
|
||||
initManagePrivilegedAppPreference();
|
||||
// this pref's default is dynamically set based on whether Orbot is installed
|
||||
boolean useTor = Preferences.get().isTorEnabled();
|
||||
useTorCheckPref.setDefaultValue(useTor);
|
||||
useTorCheckPref.setChecked(useTor);
|
||||
useTorCheckPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object enabled) {
|
||||
if ((Boolean) enabled) {
|
||||
final Activity activity = getActivity();
|
||||
enableProxyCheckPref.setEnabled(false);
|
||||
if (OrbotHelper.isOrbotInstalled(activity)) {
|
||||
NetCipher.useTor();
|
||||
} else {
|
||||
Intent intent = OrbotHelper.getOrbotInstallIntent(activity);
|
||||
activity.startActivityForResult(intent, REQUEST_INSTALL_ORBOT);
|
||||
}
|
||||
} else {
|
||||
enableProxyCheckPref.setEnabled(true);
|
||||
NetCipher.clearProxy();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
x
Reference in New Issue
Block a user