Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support tor and torsf proxies #584

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading