diff --git a/cli/onionshare_cli/common.py b/cli/onionshare_cli/common.py
index 78da8882..945a75bb 100644
--- a/cli/onionshare_cli/common.py
+++ b/cli/onionshare_cli/common.py
@@ -312,6 +312,9 @@ class Common:
# Look in resources first
base_path = self.get_resource_path("tor")
if os.path.exists(base_path):
+ self.log(
+ "Common", "get_tor_paths", f"using tor binaries in {base_path}"
+ )
tor_path = os.path.join(base_path, "tor")
tor_geo_ip_file_path = os.path.join(base_path, "geoip")
tor_geo_ipv6_file_path = os.path.join(base_path, "geoip6")
@@ -319,6 +322,9 @@ class Common:
snowflake_file_path = os.path.join(base_path, "snowflake-client")
else:
# Fallback to looking in the path
+ self.log(
+ "Common", "get_tor_paths", f"using tor binaries in system path"
+ )
tor_path = shutil.which("tor")
if not tor_path:
raise CannotFindTor()
diff --git a/cli/onionshare_cli/onion.py b/cli/onionshare_cli/onion.py
index a0f967b9..a4453651 100644
--- a/cli/onionshare_cli/onion.py
+++ b/cli/onionshare_cli/onion.py
@@ -326,6 +326,11 @@ class Onion(object):
) as o:
for line in o:
f.write(line)
+ elif self.settings.get("tor_bridges_use_snowflake"):
+ # Taken from: tor-browser_en-US/Browser/TorBrowser/Data/Tor/torrc-defaults
+ f.write(
+ f"ClientTransportPlugin snowflake exec {self.snowflake_file_path} -url https://snowflake-broker.torproject.net.global.prod.fastly.net/ -front cdn.sstatic.net -ice stun:stun.l.google.com:19302,stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478\n"
+ )
if self.settings.get("tor_bridges_use_custom_bridges"):
if "obfs4" in self.settings.get("tor_bridges_use_custom_bridges"):
diff --git a/cli/onionshare_cli/resources/torrc_template-meek_lite_amazon b/cli/onionshare_cli/resources/torrc_template-meek_lite_amazon
deleted file mode 100644
index 606ae889..00000000
--- a/cli/onionshare_cli/resources/torrc_template-meek_lite_amazon
+++ /dev/null
@@ -1,2 +0,0 @@
-Bridge meek_lite 0.0.2.0:2 B9E7141C594AF25699E0079C1F0146F409495296 url=https://d2cly7j4zqgua7.cloudfront.net/ front=a0.awsstatic.com
-UseBridges 1
\ No newline at end of file
diff --git a/cli/onionshare_cli/settings.py b/cli/onionshare_cli/settings.py
index 4755d5b3..37c00bb6 100644
--- a/cli/onionshare_cli/settings.py
+++ b/cli/onionshare_cli/settings.py
@@ -108,6 +108,7 @@ class Settings(object):
"no_bridges": True,
"tor_bridges_use_obfs4": False,
"tor_bridges_use_meek_lite_azure": False,
+ "tor_bridges_use_snowflake": False,
"tor_bridges_use_custom_bridges": "",
"persistent_tabs": [],
"locale": None, # this gets defined in fill_in_defaults()
diff --git a/desktop/src/onionshare/gui_common.py b/desktop/src/onionshare/gui_common.py
index cb10eb3b..5634d5f6 100644
--- a/desktop/src/onionshare/gui_common.py
+++ b/desktop/src/onionshare/gui_common.py
@@ -405,7 +405,21 @@ class GuiCommon:
def get_tor_paths(self):
if self.common.platform == "Linux":
- return self.common.get_tor_paths()
+ base_path = self.get_resource_path("tor")
+ if os.path.exists(base_path):
+ tor_path = os.path.join(base_path, "tor")
+ tor_geo_ip_file_path = os.path.join(base_path, "geoip")
+ tor_geo_ipv6_file_path = os.path.join(base_path, "geoip6")
+ obfs4proxy_file_path = os.path.join(base_path, "obfs4proxy")
+ snowflake_file_path = os.path.join(base_path, "snowflake-client")
+ else:
+ # Fallback to looking in the path
+ tor_path = shutil.which("tor")
+ obfs4proxy_file_path = shutil.which("obfs4proxy")
+ snowflake_file_path = shutil.which("snowflake-client")
+ prefix = os.path.dirname(os.path.dirname(tor_path))
+ tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip")
+ tor_geo_ipv6_file_path = os.path.join(prefix, "share/tor/geoip6")
if self.common.platform == "Windows":
base_path = self.get_resource_path("tor")
diff --git a/desktop/src/onionshare/resources/locale/en.json b/desktop/src/onionshare/resources/locale/en.json
index 38da16fb..3d6c8539 100644
--- a/desktop/src/onionshare/resources/locale/en.json
+++ b/desktop/src/onionshare/resources/locale/en.json
@@ -60,13 +60,13 @@
"gui_settings_authenticate_no_auth_option": "No authentication, or cookie authentication",
"gui_settings_authenticate_password_option": "Password",
"gui_settings_password_label": "Password",
- "gui_settings_tor_bridges": "Would you like to Use a Tor bridge?",
+ "gui_settings_tor_bridges": "Connect using a Tor bridge?",
+ "gui_settings_tor_bridges_label": "Bridges help you access the Tor Network in places where Tor is blocked. Depending on where you are, one bridge may work better than another.",
"gui_settings_tor_bridges_no_bridges_radio_option": "Don't use a bridge",
- "gui_settings_tor_bridges_obfs4_radio_option": "Use built-in obfs4 pluggable transports",
- "gui_settings_tor_bridges_obfs4_radio_option_no_obfs4proxy": "Use built-in obfs4 pluggable transports (requires obfs4proxy)",
- "gui_settings_tor_bridges_meek_lite_azure_radio_option": "Use built-in meek_lite (Azure) pluggable transports",
- "gui_settings_tor_bridges_meek_lite_azure_radio_option_no_obfs4proxy": "Use built-in meek_lite (Azure) pluggable transports (requires obfs4proxy)",
- "gui_settings_meek_lite_expensive_warning": "Warning: The meek_lite bridges are very costly for the Tor Project to run.
Only use them if unable to connect to Tor directly, via obfs4 transports, or other normal bridges.",
+ "gui_settings_tor_bridges_obfs4_radio_option": "Use built-in obfs4 bridge",
+ "gui_settings_tor_bridges_meek_lite_azure_radio_option": "Use built-in meek-azure bridge",
+ "gui_settings_tor_bridges_snowflake_radio_option": "Use built-in snowflake bridge",
+ "gui_settings_meek_lite_expensive_warning": "Warning: The meek-azure bridges are very costly for the Tor Project to run.
Only use them if unable to connect to Tor directly, via obfs4 transports, or other normal bridges.",
"gui_settings_tor_bridges_custom_radio_option": "Use custom bridges",
"gui_settings_tor_bridges_custom_label": "You can get bridges from https://bridges.torproject.org",
"gui_settings_tor_bridges_invalid": "None of the bridges you added work.\nDouble-check them or add others.",
diff --git a/desktop/src/onionshare/tor_settings_dialog.py b/desktop/src/onionshare/tor_settings_dialog.py
index a1b02313..00d221a7 100644
--- a/desktop/src/onionshare/tor_settings_dialog.py
+++ b/desktop/src/onionshare/tor_settings_dialog.py
@@ -89,6 +89,9 @@ class TorSettingsDialog(QtWidgets.QDialog):
# Bridge options for bundled tor
+ bridges_label = QtWidgets.QLabel(strings._("gui_settings_tor_bridges_label"))
+ bridges_label.setWordWrap(True)
+
# No bridges option radio
self.tor_bridges_no_bridges_radio = QtWidgets.QRadioButton(
strings._("gui_settings_tor_bridges_no_bridges_radio_option")
@@ -107,39 +110,55 @@ class TorSettingsDialog(QtWidgets.QDialog):
# obfs4 option radio
# if the obfs4proxy binary is missing, we can't use obfs4 transports
- if not self.obfs4proxy_file_path or not os.path.isfile(
- self.obfs4proxy_file_path
- ):
- self.tor_bridges_use_obfs4_radio = QtWidgets.QRadioButton(
- strings._("gui_settings_tor_bridges_obfs4_radio_option_no_obfs4proxy")
- )
- self.tor_bridges_use_obfs4_radio.setEnabled(False)
- else:
- self.tor_bridges_use_obfs4_radio = QtWidgets.QRadioButton(
- strings._("gui_settings_tor_bridges_obfs4_radio_option")
- )
+ self.tor_bridges_use_obfs4_radio = QtWidgets.QRadioButton(
+ strings._("gui_settings_tor_bridges_obfs4_radio_option")
+ )
self.tor_bridges_use_obfs4_radio.toggled.connect(
self.tor_bridges_use_obfs4_radio_toggled
)
-
- # meek_lite-azure option radio
- # if the obfs4proxy binary is missing, we can't use meek_lite-azure transports
if not self.obfs4proxy_file_path or not os.path.isfile(
self.obfs4proxy_file_path
):
- self.tor_bridges_use_meek_lite_azure_radio = QtWidgets.QRadioButton(
- strings._(
- "gui_settings_tor_bridges_meek_lite_azure_radio_option_no_obfs4proxy"
- )
- )
- self.tor_bridges_use_meek_lite_azure_radio.setEnabled(False)
- else:
- self.tor_bridges_use_meek_lite_azure_radio = QtWidgets.QRadioButton(
- strings._("gui_settings_tor_bridges_meek_lite_azure_radio_option")
+ self.common.log(
+ "TorSettingsDialog",
+ "__init__",
+ f"missing binary {self.obfs4proxy_file_path}, hiding obfs4 bridge",
)
+ self.tor_bridges_use_obfs4_radio.hide()
+
+ # meek-azure option radio
+ # if the obfs4proxy binary is missing, we can't use meek_lite-azure transports
+ self.tor_bridges_use_meek_lite_azure_radio = QtWidgets.QRadioButton(
+ strings._("gui_settings_tor_bridges_meek_lite_azure_radio_option")
+ )
self.tor_bridges_use_meek_lite_azure_radio.toggled.connect(
self.tor_bridges_use_meek_lite_azure_radio_toggled
)
+ if not self.obfs4proxy_file_path or not os.path.isfile(
+ self.obfs4proxy_file_path
+ ):
+ self.common.log(
+ "TorSettingsDialog",
+ "__init__",
+ f"missing binary {self.obfs4proxy_file_path}, hiding meek-azure bridge",
+ )
+ self.tor_bridges_use_meek_lite_azure_radio.hide()
+
+ # snowflake option radio
+ # if the snowflake-client binary is missing, we can't use snowflake transports
+ self.tor_bridges_use_snowflake_radio = QtWidgets.QRadioButton(
+ strings._("gui_settings_tor_bridges_snowflake_radio_option")
+ )
+ self.tor_bridges_use_snowflake_radio.toggled.connect(
+ self.tor_bridges_use_snowflake_radio_toggled
+ )
+ if not self.snowflake_file_path or not os.path.isfile(self.snowflake_file_path):
+ self.common.log(
+ "TorSettingsDialog",
+ "__init__",
+ f"missing binary {self.snowflake_file_path}, hiding snowflake bridge",
+ )
+ self.tor_bridges_use_snowflake_radio.hide()
# Custom bridges radio and textbox
self.tor_bridges_use_custom_radio = QtWidgets.QRadioButton(
@@ -178,9 +197,11 @@ class TorSettingsDialog(QtWidgets.QDialog):
# Bridges layout/widget
bridges_layout = QtWidgets.QVBoxLayout()
+ bridges_layout.addWidget(bridges_label)
bridges_layout.addWidget(self.tor_bridges_no_bridges_radio)
bridges_layout.addWidget(self.tor_bridges_use_obfs4_radio)
bridges_layout.addWidget(self.tor_bridges_use_meek_lite_azure_radio)
+ bridges_layout.addWidget(self.tor_bridges_use_snowflake_radio)
bridges_layout.addWidget(self.tor_bridges_use_custom_radio)
bridges_layout.addWidget(self.tor_bridges_use_custom_textbox_options)
@@ -411,6 +432,7 @@ class TorSettingsDialog(QtWidgets.QDialog):
self.tor_bridges_no_bridges_radio.setChecked(True)
self.tor_bridges_use_obfs4_radio.setChecked(False)
self.tor_bridges_use_meek_lite_azure_radio.setChecked(False)
+ self.tor_bridges_use_snowflake_radio.setChecked(False)
self.tor_bridges_use_custom_radio.setChecked(False)
else:
self.tor_bridges_no_bridges_radio.setChecked(False)
@@ -420,6 +442,9 @@ class TorSettingsDialog(QtWidgets.QDialog):
self.tor_bridges_use_meek_lite_azure_radio.setChecked(
self.old_settings.get("tor_bridges_use_meek_lite_azure")
)
+ self.tor_bridges_use_snowflake_radio.setChecked(
+ self.old_settings.get("tor_bridges_use_snowflake")
+ )
if self.old_settings.get("tor_bridges_use_custom_bridges"):
self.tor_bridges_use_custom_radio.setChecked(True)
@@ -473,6 +498,13 @@ class TorSettingsDialog(QtWidgets.QDialog):
QtWidgets.QMessageBox.Warning,
)
+ def tor_bridges_use_snowflake_radio_toggled(self, checked):
+ """
+ snowflake bridges option was toggled. If checked, disable custom bridge options.
+ """
+ if checked:
+ self.tor_bridges_use_custom_textbox_options.hide()
+
def tor_bridges_use_custom_radio_toggled(self, checked):
"""
Custom bridges option was toggled. If checked, show custom bridge options.
@@ -712,21 +744,31 @@ class TorSettingsDialog(QtWidgets.QDialog):
settings.set("no_bridges", True)
settings.set("tor_bridges_use_obfs4", False)
settings.set("tor_bridges_use_meek_lite_azure", False)
+ settings.set("tor_bridges_use_snowflake", False)
settings.set("tor_bridges_use_custom_bridges", "")
if self.tor_bridges_use_obfs4_radio.isChecked():
settings.set("no_bridges", False)
settings.set("tor_bridges_use_obfs4", True)
settings.set("tor_bridges_use_meek_lite_azure", False)
+ settings.set("tor_bridges_use_snowflake", False)
settings.set("tor_bridges_use_custom_bridges", "")
if self.tor_bridges_use_meek_lite_azure_radio.isChecked():
settings.set("no_bridges", False)
settings.set("tor_bridges_use_obfs4", False)
settings.set("tor_bridges_use_meek_lite_azure", True)
+ settings.set("tor_bridges_use_snowflake", False)
+ settings.set("tor_bridges_use_custom_bridges", "")
+ if self.tor_bridges_use_snowflake_radio.isChecked():
+ settings.set("no_bridges", False)
+ settings.set("tor_bridges_use_obfs4", False)
+ settings.set("tor_bridges_use_meek_lite_azure", False)
+ settings.set("tor_bridges_use_snowflake", True)
settings.set("tor_bridges_use_custom_bridges", "")
if self.tor_bridges_use_custom_radio.isChecked():
settings.set("no_bridges", False)
settings.set("tor_bridges_use_obfs4", False)
settings.set("tor_bridges_use_meek_lite_azure", False)
+ settings.set("tor_bridges_use_snowflake", False)
# Insert a 'Bridge' line at the start of each bridge.
# This makes it easier to copy/paste a set of bridges