Skip to content

Commit

Permalink
feat: support tor and torsf proxies
Browse files Browse the repository at this point in the history
Unfortunately, because of ooni/probe#2406, we
are going to see crashes when using these proxies.

This diff is part of ooni/probe#2500. Since we're
increasingly being blocked, it makes sense to exposes all the possible
proxies we can feature.

We're going to touch upon the same files again once we land the
ooni/probe-cli#1162 pull request.
  • Loading branch information
bassosimone committed Jul 12, 2023
1 parent 00e0754 commit 1f24533
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
*
Expand All @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
public enum ProxyProtocol {
NONE("none"),
PSIPHON("psiphon"),
TOR("tor"),
TORSF("torsf"),
SOCKS5("socks5");

private String protocol;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand All @@ -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");
}
Expand Down Expand Up @@ -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).
Expand Down
16 changes: 15 additions & 1 deletion app/src/main/res/layout/activity_proxy.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@
android:textColor="@color/color_black"
android:text="@string/Settings_Proxy_Psiphon" />

<RadioButton
android:id="@+id/proxyTor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/color_black"
android:text="@string/Settings_Proxy_Tor" />

<RadioButton
android:id="@+id/proxyTorSf"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/color_black"
android:text="@string/Settings_Proxy_TorSf" />

<RadioButton
android:id="@+id/proxyCustom"
android:layout_width="wrap_content"
Expand Down Expand Up @@ -114,4 +128,4 @@
</LinearLayout>
</ScrollView>

</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,8 @@
<string name="Settings_Proxy_Enabled">Proxy</string>
<string name="Settings_Proxy_None">None</string>
<string name="Settings_Proxy_Psiphon">Psiphon</string>
<string name="Settings_Proxy_Tor">Tor (vanilla)</string>
<string name="Settings_Proxy_TorSf">Tor with Snowflake</string>
<string name="Settings_Proxy_Custom">Custom Proxy</string>
<string name="Settings_Proxy_Custom_Value">Custom Proxy URL</string>
<string name="Settings_Proxy_Custom_Protocol">Custom proxy protocol</string>
Expand Down

0 comments on commit 1f24533

Please sign in to comment.