mirror of
https://github.com/onionshare/onionshare.git
synced 2025-02-03 01:50:11 -05:00
Merge branch 'censorship_automatically_attempt_and_reconnect' of https://github.com/mig5/onionshare into mig5-censorship_automatically_attempt_and_reconnect
This commit is contained in:
commit
75d286386b
@ -198,3 +198,65 @@ class CensorshipCircumvention(object):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def save_settings(self, settings, bridge_settings):
|
||||||
|
"""
|
||||||
|
Checks the bridges and saves them in settings.
|
||||||
|
"""
|
||||||
|
bridges_ok = False
|
||||||
|
self.settings = settings
|
||||||
|
|
||||||
|
# @TODO there might be several bridge types recommended.
|
||||||
|
# Should we attempt to iterate over each type if one of them fails to connect?
|
||||||
|
# But if so, how to stop it starting 3 separate Tor connection threads?
|
||||||
|
# for bridges in request_bridges["settings"]:
|
||||||
|
bridges = bridge_settings["settings"][0]["bridges"]
|
||||||
|
self.common.log(
|
||||||
|
"CensorshipCircumvention",
|
||||||
|
"save_settings",
|
||||||
|
f"Obtained bridges: {bridges}",
|
||||||
|
)
|
||||||
|
bridge_strings = bridges["bridge_strings"]
|
||||||
|
bridge_type = bridges["type"]
|
||||||
|
bridge_source = bridges["source"]
|
||||||
|
|
||||||
|
# If the recommended bridge source is to use the built-in
|
||||||
|
# bridges, set that in our settings, as if the user had
|
||||||
|
# selected the built-in bridges for a specific PT themselves.
|
||||||
|
#
|
||||||
|
if bridge_source == "builtin":
|
||||||
|
self.settings.set("bridges_type", "built-in")
|
||||||
|
if bridge_type == "obfs4":
|
||||||
|
self.settings.set("bridges_builtin_pt", "obfs4")
|
||||||
|
if bridge_type == "snowflake":
|
||||||
|
self.settings.set("bridges_builtin_pt", "snowflake")
|
||||||
|
if bridge_type == "meek":
|
||||||
|
self.settings.set("bridges_builtin_pt", "meek-azure")
|
||||||
|
bridges_ok = True
|
||||||
|
else:
|
||||||
|
# Any other type of bridge we can treat as custom.
|
||||||
|
self.settings.set("bridges_type", "custom")
|
||||||
|
|
||||||
|
# Sanity check the bridges provided from the Tor API before saving
|
||||||
|
bridges_checked = self.common.check_bridges_valid(bridge_strings)
|
||||||
|
|
||||||
|
if bridges_checked:
|
||||||
|
self.settings.set("bridges_custom", "\n".join(bridges_checked))
|
||||||
|
bridges_ok = True
|
||||||
|
|
||||||
|
# If we got any good bridges, save them to settings and return.
|
||||||
|
if bridges_ok:
|
||||||
|
self.common.log(
|
||||||
|
"CensorshipCircumvention",
|
||||||
|
"save_settings",
|
||||||
|
"Saving settings with automatically-obtained bridges",
|
||||||
|
)
|
||||||
|
self.settings.save()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.common.log(
|
||||||
|
"CensorshipCircumvention",
|
||||||
|
"save_settings",
|
||||||
|
"Could not use any of the obtained bridges.",
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
@ -28,6 +28,7 @@ import sys
|
|||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import shutil
|
import shutil
|
||||||
|
import re
|
||||||
from pkg_resources import resource_filename
|
from pkg_resources import resource_filename
|
||||||
|
|
||||||
import colorama
|
import colorama
|
||||||
@ -441,6 +442,40 @@ class Common:
|
|||||||
r = random.SystemRandom()
|
r = random.SystemRandom()
|
||||||
return "-".join(r.choice(wordlist) for _ in range(word_count))
|
return "-".join(r.choice(wordlist) for _ in range(word_count))
|
||||||
|
|
||||||
|
def check_bridges_valid(self, bridges):
|
||||||
|
"""
|
||||||
|
Does a regex check against a supplied list of bridges, to make sure they
|
||||||
|
are valid strings depending on the bridge type.
|
||||||
|
"""
|
||||||
|
valid_bridges = []
|
||||||
|
self.log("Common", "check_bridges_valid", "Checking bridge syntax")
|
||||||
|
for bridge in bridges:
|
||||||
|
if bridge != "":
|
||||||
|
# Check the syntax of the custom bridge to make sure it looks legitimate
|
||||||
|
ipv4_pattern = re.compile(
|
||||||
|
"(obfs4\s+)?(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]):([0-9]+)(\s+)([A-Z0-9]+)(.+)$"
|
||||||
|
)
|
||||||
|
ipv6_pattern = re.compile(
|
||||||
|
"(obfs4\s+)?\[(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\]:[0-9]+\s+[A-Z0-9]+(.+)$"
|
||||||
|
)
|
||||||
|
meek_lite_pattern = re.compile(
|
||||||
|
"(meek_lite)(\s)+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+)(\s)+([0-9A-Z]+)(\s)+url=(.+)(\s)+front=(.+)"
|
||||||
|
)
|
||||||
|
snowflake_pattern = re.compile(
|
||||||
|
"(snowflake)(\s)+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+)(\s)+([0-9A-Z]+)"
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
ipv4_pattern.match(bridge)
|
||||||
|
or ipv6_pattern.match(bridge)
|
||||||
|
or meek_lite_pattern.match(bridge)
|
||||||
|
or snowflake_pattern.match(bridge)
|
||||||
|
):
|
||||||
|
valid_bridges.append(bridge)
|
||||||
|
if valid_bridges:
|
||||||
|
return valid_bridges
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def is_flatpak(self):
|
def is_flatpak(self):
|
||||||
"""
|
"""
|
||||||
Returns True if OnionShare is running in a Flatpak sandbox
|
Returns True if OnionShare is running in a Flatpak sandbox
|
||||||
@ -453,6 +488,7 @@ class Common:
|
|||||||
"""
|
"""
|
||||||
return os.environ.get("SNAP_INSTANCE_NAME") == "onionshare"
|
return os.environ.get("SNAP_INSTANCE_NAME") == "onionshare"
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def random_string(num_bytes, output_len=None):
|
def random_string(num_bytes, output_len=None):
|
||||||
"""
|
"""
|
||||||
|
@ -114,6 +114,7 @@ class Settings(object):
|
|||||||
"persistent_tabs": [],
|
"persistent_tabs": [],
|
||||||
"locale": None, # this gets defined in fill_in_defaults()
|
"locale": None, # this gets defined in fill_in_defaults()
|
||||||
"theme": 0,
|
"theme": 0,
|
||||||
|
"censorship_circumvention": False,
|
||||||
}
|
}
|
||||||
self._settings = {}
|
self._settings = {}
|
||||||
self.fill_in_defaults()
|
self.fill_in_defaults()
|
||||||
|
@ -37,6 +37,7 @@ class TestSettings:
|
|||||||
"bridges_builtin": {},
|
"bridges_builtin": {},
|
||||||
"persistent_tabs": [],
|
"persistent_tabs": [],
|
||||||
"theme": 0,
|
"theme": 0,
|
||||||
|
"censorship_circumvention": False,
|
||||||
}
|
}
|
||||||
for key in settings_obj._settings:
|
for key in settings_obj._settings:
|
||||||
# Skip locale, it will not always default to the same thing
|
# Skip locale, it will not always default to the same thing
|
||||||
|
@ -30,6 +30,7 @@ from .tab_widget import TabWidget
|
|||||||
from .gui_common import GuiCommon
|
from .gui_common import GuiCommon
|
||||||
from .threads import OnionCleanupThread
|
from .threads import OnionCleanupThread
|
||||||
|
|
||||||
|
from onionshare_cli.meek import Meek
|
||||||
|
|
||||||
class MainWindow(QtWidgets.QMainWindow):
|
class MainWindow(QtWidgets.QMainWindow):
|
||||||
"""
|
"""
|
||||||
@ -160,8 +161,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.setCentralWidget(central_widget)
|
self.setCentralWidget(central_widget)
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
|
# Instantiate Meek, which the TorConnectionDialog may use to resolve
|
||||||
|
# connection issues by automatically obtaining bridges.
|
||||||
|
self.meek = Meek(self.common, get_tor_paths=self.common.gui.get_tor_paths)
|
||||||
# Start the "Connecting to Tor" dialog, which calls onion.connect()
|
# Start the "Connecting to Tor" dialog, which calls onion.connect()
|
||||||
tor_con = TorConnectionDialog(self.common)
|
tor_con = TorConnectionDialog(self.common, self.meek)
|
||||||
tor_con.canceled.connect(self.tor_connection_canceled)
|
tor_con.canceled.connect(self.tor_connection_canceled)
|
||||||
tor_con.open_tor_settings.connect(self.tor_connection_open_tor_settings)
|
tor_con.open_tor_settings.connect(self.tor_connection_open_tor_settings)
|
||||||
if not self.common.gui.local_only:
|
if not self.common.gui.local_only:
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
"gui_settings_tor_bridges": "Connect using 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_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_bridge_use_checkbox": "Use a bridge",
|
"gui_settings_bridge_use_checkbox": "Use a bridge",
|
||||||
|
"gui_settings_censorship_circumvention_checkbox": "Attempt to automatically find a bridge based on your country if Tor fails to connect",
|
||||||
"gui_settings_bridge_radio_builtin": "Select a built-in bridge",
|
"gui_settings_bridge_radio_builtin": "Select a built-in bridge",
|
||||||
"gui_settings_bridge_none_radio_option": "Don't use a bridge",
|
"gui_settings_bridge_none_radio_option": "Don't use a bridge",
|
||||||
"gui_settings_meek_lite_expensive_warning": "Warning: The meek-azure bridges are very costly for the Tor Project to run.<br><br>Only use them if unable to connect to Tor directly, via obfs4 transports, or other normal bridges.",
|
"gui_settings_meek_lite_expensive_warning": "Warning: The meek-azure bridges are very costly for the Tor Project to run.<br><br>Only use them if unable to connect to Tor directly, via obfs4 transports, or other normal bridges.",
|
||||||
|
@ -41,6 +41,7 @@ from onionshare_cli.onion import (
|
|||||||
from . import strings
|
from . import strings
|
||||||
from .gui_common import GuiCommon
|
from .gui_common import GuiCommon
|
||||||
from .widgets import Alert
|
from .widgets import Alert
|
||||||
|
from onionshare_cli.censorship import CensorshipCircumvention
|
||||||
|
|
||||||
|
|
||||||
class TorConnectionDialog(QtWidgets.QProgressDialog):
|
class TorConnectionDialog(QtWidgets.QProgressDialog):
|
||||||
@ -52,13 +53,15 @@ class TorConnectionDialog(QtWidgets.QProgressDialog):
|
|||||||
success = QtCore.Signal()
|
success = QtCore.Signal()
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, common, custom_settings=False, testing_settings=False, onion=None
|
self, common, meek, custom_settings=False, testing_settings=False, onion=None
|
||||||
):
|
):
|
||||||
super(TorConnectionDialog, self).__init__(None)
|
super(TorConnectionDialog, self).__init__(None)
|
||||||
|
|
||||||
self.common = common
|
self.common = common
|
||||||
self.testing_settings = testing_settings
|
self.testing_settings = testing_settings
|
||||||
|
|
||||||
|
self.meek = meek
|
||||||
|
|
||||||
if custom_settings:
|
if custom_settings:
|
||||||
self.settings = custom_settings
|
self.settings = custom_settings
|
||||||
else:
|
else:
|
||||||
@ -137,6 +140,30 @@ class TorConnectionDialog(QtWidgets.QProgressDialog):
|
|||||||
def alert():
|
def alert():
|
||||||
Alert(self.common, msg, QtWidgets.QMessageBox.Warning, title=self.title)
|
Alert(self.common, msg, QtWidgets.QMessageBox.Warning, title=self.title)
|
||||||
|
|
||||||
|
# If we are allowed to try automatically resolving connection issues
|
||||||
|
# (e.g possible censorship) by obtaining bridges for the user, do so
|
||||||
|
elif self.settings.get("censorship_circumvention"):
|
||||||
|
def alert():
|
||||||
|
return
|
||||||
|
|
||||||
|
# Automatically try to obtain bridges from the Censorship Circumvention API
|
||||||
|
self.common.log(
|
||||||
|
"TorConnectionDialog",
|
||||||
|
"_error_connecting_to_tor",
|
||||||
|
"Trying to automatically obtain bridges",
|
||||||
|
)
|
||||||
|
self.meek.start()
|
||||||
|
self.censorship_circumvention = CensorshipCircumvention(
|
||||||
|
self.common, self.meek
|
||||||
|
)
|
||||||
|
bridge_settings = self.censorship_circumvention.request_settings(
|
||||||
|
country="tm"
|
||||||
|
)
|
||||||
|
self.meek.cleanup()
|
||||||
|
|
||||||
|
if bridge_settings and self.censorship_circumvention.save_settings(self.settings, bridge_settings):
|
||||||
|
# Try and connect again
|
||||||
|
self.start()
|
||||||
else:
|
else:
|
||||||
# If not testing, open settings after displaying the error
|
# If not testing, open settings after displaying the error
|
||||||
def alert():
|
def alert():
|
||||||
@ -165,7 +192,7 @@ class TorConnectionWidget(QtWidgets.QWidget):
|
|||||||
success = QtCore.Signal()
|
success = QtCore.Signal()
|
||||||
fail = QtCore.Signal(str)
|
fail = QtCore.Signal(str)
|
||||||
|
|
||||||
def __init__(self, common, status_bar):
|
def __init__(self, common, status_bar, meek):
|
||||||
super(TorConnectionWidget, self).__init__(None)
|
super(TorConnectionWidget, self).__init__(None)
|
||||||
self.common = common
|
self.common = common
|
||||||
self.common.log("TorConnectionWidget", "__init__")
|
self.common.log("TorConnectionWidget", "__init__")
|
||||||
@ -181,6 +208,8 @@ class TorConnectionWidget(QtWidgets.QWidget):
|
|||||||
)
|
)
|
||||||
self.cancel_button.clicked.connect(self.cancel_clicked)
|
self.cancel_button.clicked.connect(self.cancel_clicked)
|
||||||
|
|
||||||
|
self.meek = meek
|
||||||
|
|
||||||
progress_layout = QtWidgets.QHBoxLayout()
|
progress_layout = QtWidgets.QHBoxLayout()
|
||||||
progress_layout.addWidget(self.progress)
|
progress_layout.addWidget(self.progress)
|
||||||
progress_layout.addWidget(self.cancel_button)
|
progress_layout.addWidget(self.cancel_button)
|
||||||
@ -263,7 +292,32 @@ class TorConnectionWidget(QtWidgets.QWidget):
|
|||||||
def _error_connecting_to_tor(self, msg):
|
def _error_connecting_to_tor(self, msg):
|
||||||
self.common.log("TorConnectionWidget", "_error_connecting_to_tor")
|
self.common.log("TorConnectionWidget", "_error_connecting_to_tor")
|
||||||
self.active = False
|
self.active = False
|
||||||
self.fail.emit(msg)
|
|
||||||
|
# If we are allowed to try automatically resolving connection issues
|
||||||
|
# (e.g possible censorship) by obtaining bridges for the user, do so
|
||||||
|
if self.settings.get("censorship_circumvention"):
|
||||||
|
# Automatically try to obtain bridges from the Censorship Circumvention API
|
||||||
|
self.common.log(
|
||||||
|
"TorConnectionWidget",
|
||||||
|
"_error_connecting_to_tor",
|
||||||
|
"Trying to automatically obtain bridges",
|
||||||
|
)
|
||||||
|
self.meek.start()
|
||||||
|
self.censorship_circumvention = CensorshipCircumvention(
|
||||||
|
self.common, self.meek
|
||||||
|
)
|
||||||
|
bridge_settings = self.censorship_circumvention.request_settings(
|
||||||
|
country="tm"
|
||||||
|
)
|
||||||
|
self.meek.cleanup()
|
||||||
|
|
||||||
|
if bridge_settings and self.censorship_circumvention.save_settings(self.settings, bridge_settings):
|
||||||
|
# Try and connect again
|
||||||
|
self.start()
|
||||||
|
else:
|
||||||
|
self.fail.emit()
|
||||||
|
else:
|
||||||
|
self.fail.emit()
|
||||||
|
|
||||||
|
|
||||||
class TorConnectionThread(QtCore.QThread):
|
class TorConnectionThread(QtCore.QThread):
|
||||||
|
@ -21,7 +21,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
from PySide2 import QtCore, QtWidgets, QtGui
|
from PySide2 import QtCore, QtWidgets, QtGui
|
||||||
import sys
|
import sys
|
||||||
import platform
|
import platform
|
||||||
import re
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from onionshare_cli.meek import Meek
|
from onionshare_cli.meek import Meek
|
||||||
@ -91,6 +90,12 @@ class TorSettingsTab(QtWidgets.QWidget):
|
|||||||
self.bridge_use_checkbox.stateChanged.connect(
|
self.bridge_use_checkbox.stateChanged.connect(
|
||||||
self.bridge_use_checkbox_state_changed
|
self.bridge_use_checkbox_state_changed
|
||||||
)
|
)
|
||||||
|
self.censorship_circumvention_checkbox = QtWidgets.QCheckBox(
|
||||||
|
strings._("gui_settings_censorship_circumvention_checkbox")
|
||||||
|
)
|
||||||
|
self.censorship_circumvention_checkbox.stateChanged.connect(
|
||||||
|
self.censorship_circumvention_checkbox_state_changed
|
||||||
|
)
|
||||||
|
|
||||||
# Built-in bridge
|
# Built-in bridge
|
||||||
self.bridge_builtin_radio = QtWidgets.QRadioButton(
|
self.bridge_builtin_radio = QtWidgets.QRadioButton(
|
||||||
@ -164,6 +169,7 @@ class TorSettingsTab(QtWidgets.QWidget):
|
|||||||
bridges_layout = QtWidgets.QVBoxLayout()
|
bridges_layout = QtWidgets.QVBoxLayout()
|
||||||
bridges_layout.addWidget(bridges_label)
|
bridges_layout.addWidget(bridges_label)
|
||||||
bridges_layout.addWidget(self.bridge_use_checkbox)
|
bridges_layout.addWidget(self.bridge_use_checkbox)
|
||||||
|
bridges_layout.addWidget(self.censorship_circumvention_checkbox)
|
||||||
bridges_layout.addWidget(self.bridge_settings)
|
bridges_layout.addWidget(self.bridge_settings)
|
||||||
|
|
||||||
self.bridges = QtWidgets.QWidget()
|
self.bridges = QtWidgets.QWidget()
|
||||||
@ -330,7 +336,7 @@ class TorSettingsTab(QtWidgets.QWidget):
|
|||||||
columns_wrapper.setLayout(columns_layout)
|
columns_wrapper.setLayout(columns_layout)
|
||||||
|
|
||||||
# Tor connection widget
|
# Tor connection widget
|
||||||
self.tor_con = TorConnectionWidget(self.common, self.status_bar)
|
self.tor_con = TorConnectionWidget(self.common, self.status_bar, self.meek)
|
||||||
self.tor_con.success.connect(self.tor_con_success)
|
self.tor_con.success.connect(self.tor_con_success)
|
||||||
self.tor_con.fail.connect(self.tor_con_fail)
|
self.tor_con.fail.connect(self.tor_con_fail)
|
||||||
self.tor_con.hide()
|
self.tor_con.hide()
|
||||||
@ -430,6 +436,7 @@ class TorSettingsTab(QtWidgets.QWidget):
|
|||||||
|
|
||||||
if self.old_settings.get("bridges_enabled"):
|
if self.old_settings.get("bridges_enabled"):
|
||||||
self.bridge_use_checkbox.setCheckState(QtCore.Qt.Checked)
|
self.bridge_use_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
|
self.censorship_circumvention_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
self.bridge_settings.show()
|
self.bridge_settings.show()
|
||||||
|
|
||||||
bridges_type = self.old_settings.get("bridges_type")
|
bridges_type = self.old_settings.get("bridges_type")
|
||||||
@ -506,6 +513,16 @@ class TorSettingsTab(QtWidgets.QWidget):
|
|||||||
else:
|
else:
|
||||||
self.bridge_settings.hide()
|
self.bridge_settings.hide()
|
||||||
|
|
||||||
|
def censorship_circumvention_checkbox_state_changed(self, state):
|
||||||
|
"""
|
||||||
|
'Allow censorship circumvention (automatic bridges)' checkbox changed
|
||||||
|
"""
|
||||||
|
# Turning on censorship circumvention through the act of
|
||||||
|
# automatic bridge selection, implicitly means enabling
|
||||||
|
# bridges.
|
||||||
|
if state == QtCore.Qt.Checked:
|
||||||
|
self.bridge_use_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
|
|
||||||
def bridge_builtin_radio_toggled(self, checked):
|
def bridge_builtin_radio_toggled(self, checked):
|
||||||
"""
|
"""
|
||||||
'Select a built-in bridge' radio button toggled
|
'Select a built-in bridge' radio button toggled
|
||||||
@ -812,6 +829,9 @@ class TorSettingsTab(QtWidgets.QWidget):
|
|||||||
if self.bridge_use_checkbox.checkState() == QtCore.Qt.Checked:
|
if self.bridge_use_checkbox.checkState() == QtCore.Qt.Checked:
|
||||||
settings.set("bridges_enabled", True)
|
settings.set("bridges_enabled", True)
|
||||||
|
|
||||||
|
if self.censorship_circumvention_checkbox.checkState() == QtCore.Qt.Checked:
|
||||||
|
settings.set("censorship_circumvention", True)
|
||||||
|
|
||||||
if self.bridge_builtin_radio.isChecked():
|
if self.bridge_builtin_radio.isChecked():
|
||||||
settings.set("bridges_type", "built-in")
|
settings.set("bridges_type", "built-in")
|
||||||
|
|
||||||
@ -835,35 +855,10 @@ class TorSettingsTab(QtWidgets.QWidget):
|
|||||||
if self.bridge_custom_radio.isChecked():
|
if self.bridge_custom_radio.isChecked():
|
||||||
settings.set("bridges_type", "custom")
|
settings.set("bridges_type", "custom")
|
||||||
|
|
||||||
new_bridges = []
|
|
||||||
bridges = self.bridge_custom_textbox.toPlainText().split("\n")
|
bridges = self.bridge_custom_textbox.toPlainText().split("\n")
|
||||||
bridges_valid = False
|
bridges_valid = self.common.check_bridges_valid(bridges)
|
||||||
for bridge in bridges:
|
|
||||||
if bridge != "":
|
|
||||||
# Check the syntax of the custom bridge to make sure it looks legitimate
|
|
||||||
ipv4_pattern = re.compile(
|
|
||||||
"(obfs4\s+)?(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]):([0-9]+)(\s+)([A-Z0-9]+)(.+)$"
|
|
||||||
)
|
|
||||||
ipv6_pattern = re.compile(
|
|
||||||
"(obfs4\s+)?\[(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\]:[0-9]+\s+[A-Z0-9]+(.+)$"
|
|
||||||
)
|
|
||||||
meek_lite_pattern = re.compile(
|
|
||||||
"(meek_lite)(\s)+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+)(\s)+([0-9A-Z]+)(\s)+url=(.+)(\s)+front=(.+)"
|
|
||||||
)
|
|
||||||
snowflake_pattern = re.compile(
|
|
||||||
"(snowflake)(\s)+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+)(\s)+([0-9A-Z]+)"
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
ipv4_pattern.match(bridge)
|
|
||||||
or ipv6_pattern.match(bridge)
|
|
||||||
or meek_lite_pattern.match(bridge)
|
|
||||||
or snowflake_pattern.match(bridge)
|
|
||||||
):
|
|
||||||
new_bridges.append(bridge)
|
|
||||||
bridges_valid = True
|
|
||||||
|
|
||||||
if bridges_valid:
|
if bridges_valid:
|
||||||
new_bridges = "\n".join(new_bridges) + "\n"
|
new_bridges = "\n".join(bridges_valid) + "\n"
|
||||||
settings.set("bridges_custom", new_bridges)
|
settings.set("bridges_custom", new_bridges)
|
||||||
else:
|
else:
|
||||||
self.error_label.setText(
|
self.error_label.setText(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user