diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/ProxyActivity.java b/app/src/main/java/org/openobservatory/ooniprobe/activity/ProxyActivity.java index 46ced49d6..f6c562043 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/ProxyActivity.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/ProxyActivity.java @@ -48,9 +48,15 @@ public class ProxyActivity extends AbstractActivity { * * 1. an empty string means no proxy; * - * 2. "psiphon://" means that we wanna use psiphon; + * 2. "psiphon:///" means that we want to use psiphon; * - * 3. "socks5://1.2.3.4:5678" or "socks5://[::1]:5678" or "socks5://d.com:5678" + * 3. "tor:///" means we want to use tor without any proxy (which is possible + * because oonimkall embeds `libtor.a` as a dependency); + * + * 4. "torsf:///" is like "tor:///" but additionally uses the snowflake + * library we also bundle inside of oonimkall; + * + * 5. "socks5://1.2.3.4:5678" or "socks5://[::1]:5678" or "socks5://d.com:5678" * means that we wanna use the given socks5 proxy. * * Future improvements @@ -67,8 +73,6 @@ public class ProxyActivity extends AbstractActivity { * This implies we can trivially support a vanilla socks5 proxy with username and * password by just replacing `psiphon+socks5` with `socks5`. * - * We also want to support vanilla tor, using `tor://`. - * * We also want to support vanilla tor with socks5, which is trivially doable * using as a scheme the `tor+socks5` scheme. * @@ -93,15 +97,20 @@ public class ProxyActivity extends AbstractActivity { * The design and implementation of this class owes to the code contributed * by and the suggestion from friendly anonymous users. Thank you! */ + + // logger is the injected AppLogger instance. @Inject AppLogger logger; + // TAG is the tag used for logging. private final static String TAG = "ProxyActivity"; + // preferenceManager is the injected PreferenceManager instance. @Inject PreferenceManager preferenceManager; - // The following radio group describes the top level choice - // in terms of proxying: no proxy, psiphon, or custom. + + // The following radio group describes the top level choice in terms of + // proxying: no proxy, psiphon, tor, torsf, or custom. // proxyRadioGroup is the top-level radio group. private RadioGroup proxyRadioGroup; @@ -112,12 +121,21 @@ public class ProxyActivity extends AbstractActivity { // proxyPsiphonRB is the radio button selecting the "psiphon" proxy. private RadioButton proxyPsiphonRB; + // proxyTorRB is the radio button selecting the "tor" proxy. + private RadioButton proxyTorRB; + + // proxyTorSfRB is the radio button selecting the "torsf" proxy. + private RadioButton proxyTorSfRB; + // proxyCustomRB is the radio button for the "custom" proxy. private RadioButton proxyCustomRB; // The following radio group allows users to choose which specific // custom proxy they would like to use. When writing this documentation, // only socks5 is available but we will add more options. + // + // TODO(bassosimone): we need to implement support for HTTP proxies + // once https://github.com/ooni/probe-cli/pull/1162 lands. // customProxyRadioGroup allows you to choose among the different // kinds of custom proxies that are available. @@ -153,6 +171,8 @@ public void onCreate(Bundle savedInstanceState) { proxyRadioGroup = findViewById(R.id.proxyRadioGroup); proxyNoneRB = findViewById(R.id.proxyNone); proxyPsiphonRB = findViewById(R.id.proxyPsiphon); + proxyTorRB = findViewById(R.id.proxyTor); + proxyTorSfRB = findViewById(R.id.proxyTorSf); proxyCustomRB = findViewById(R.id.proxyCustom); customProxyRadioGroup = findViewById(R.id.customProxyRadioGroup); customProxySOCKS5 = findViewById(R.id.customProxySOCKS5); @@ -196,6 +216,10 @@ private void configureInitialViewWithSettings(ProxySettings settings) { proxyNoneRB.setChecked(true); } else if (settings.protocol == ProxyProtocol.PSIPHON) { proxyPsiphonRB.setChecked(true); + } else if (settings.protocol == ProxyProtocol.TOR) { + proxyTorRB.setChecked(true); + } else if (settings.protocol == ProxyProtocol.TORSF) { + proxyTorSfRB.setChecked(true); } else if (settings.protocol == ProxyProtocol.SOCKS5) { proxyCustomRB.setChecked(true); } else { @@ -226,6 +250,10 @@ private void configureInitialViewWithSettings(ProxySettings settings) { customProxySetEnabled(false); } else if (checkedId == R.id.proxyPsiphon) { customProxySetEnabled(false); + } else if (checkedId == R.id.proxyTor) { + customProxySetEnabled(false); + } else if (checkedId == R.id.proxyTorSf) { + customProxySetEnabled(false); } else if (checkedId == R.id.proxyCustom) { customProxySetEnabled(true); customProxyRadioGroup.clearCheck(); @@ -364,6 +392,24 @@ public void onBackPressed() { return; } + // If the tor proxy is checked then write back the right + // proxy configuration for tor and move on. + if (proxyTorRB.isChecked()) { + settings.protocol = ProxyProtocol.TOR; + saveSettings(); + super.onBackPressed(); + return; + } + + // If the torsf proxy is checked then write back the right + // proxy configuration for torsf and move on. + if (proxyTorSfRB.isChecked()) { + settings.protocol = ProxyProtocol.TORSF; + saveSettings(); + super.onBackPressed(); + return; + } + // validate the hostname for the custom proxy. if (!isValidHostnameOrIP(hostname)) { customProxyHostname.setError("not a valid hostname or IP"); diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/ProxyProtocol.java b/app/src/main/java/org/openobservatory/ooniprobe/common/ProxyProtocol.java index ed5742a2e..ee7e8614e 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/common/ProxyProtocol.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/common/ProxyProtocol.java @@ -4,6 +4,8 @@ public enum ProxyProtocol { NONE("none"), PSIPHON("psiphon"), + TOR("tor"), + TORSF("torsf"), SOCKS5("socks5"); private String protocol; diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/ProxySettings.java b/app/src/main/java/org/openobservatory/ooniprobe/common/ProxySettings.java index e19867f94..78404b833 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/common/ProxySettings.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/common/ProxySettings.java @@ -13,8 +13,7 @@ * documentation of proxy activity for the design rationale. */ public class ProxySettings { - - /** scheme is the proxy scheme (e.g., "psiphon", "socks5"). */ + /** scheme is the proxy scheme (e.g., "psiphon", "tor", "torsf", "socks5"). */ public ProxyProtocol protocol = ProxyProtocol.NONE; /** hostname is the hostname for custom proxies. */ @@ -33,10 +32,14 @@ public static ProxySettings newProxySettings(PreferenceManager pm) throws Invali settings.protocol = ProxyProtocol.NONE; } else if (protocol.equals(ProxyProtocol.PSIPHON.getProtocol())) { settings.protocol = ProxyProtocol.PSIPHON; + } else if (protocol.equals(ProxyProtocol.TOR.getProtocol())) { + settings.protocol = ProxyProtocol.TOR; + } else if (protocol.equals(ProxyProtocol.TORSF.getProtocol())) { + settings.protocol = ProxyProtocol.TORSF; } else if (protocol.equals(ProxyProtocol.SOCKS5.getProtocol())) { settings.protocol = ProxyProtocol.SOCKS5; } else { - // This is where we will extend the code to add support for + // This exception indicates that we need to extend the code to support // more proxies, e.g., HTTP proxies. throw new InvalidProxyURL("unhandled URL scheme"); } @@ -72,10 +75,18 @@ private boolean isIPv6(String hostname) { /** getProxyString returns to you the proxy string you should pass to oonimkall. */ public String getProxyString() throws URISyntaxException { - if (protocol == ProxyProtocol.NONE) + if (protocol == ProxyProtocol.NONE) { return ""; - if (protocol == ProxyProtocol.PSIPHON) - return "psiphon://"; + } + if (protocol == ProxyProtocol.PSIPHON) { + return "psiphon:///"; + } + if (protocol == ProxyProtocol.TOR) { + return "tor:///"; + } + if (protocol == ProxyProtocol.TORSF) { + return "torsf:///"; + } if (protocol == ProxyProtocol.SOCKS5) { // Alright, we now need to construct a new SOCKS5 URL. We are going to defer // doing that to the Java standard library (er, the Android stdlib). diff --git a/app/src/main/res/layout/activity_proxy.xml b/app/src/main/res/layout/activity_proxy.xml index d3c565ed4..6b7da69b1 100644 --- a/app/src/main/res/layout/activity_proxy.xml +++ b/app/src/main/res/layout/activity_proxy.xml @@ -38,6 +38,20 @@ android:textColor="@color/color_black" android:text="@string/Settings_Proxy_Psiphon" /> + + + + - \ No newline at end of file + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index db87e2f35..a9a48c990 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -392,6 +392,8 @@ Proxy None Psiphon + Tor (vanilla) + Tor with Snowflake Custom Proxy Custom Proxy URL Custom proxy protocol