Browse Source

Merge pull request #8714 from accumulator/tor_probe

network: async tor probe
master
accumulator 2 years ago committed by GitHub
parent
commit
134fd6c656
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      electrum/gui/qml/qenetwork.py
  2. 16
      electrum/gui/qt/main_window.py
  3. 2
      electrum/gui/qt/network_dialog.py
  4. 32
      electrum/network.py
  5. 4
      electrum/util.py

6
electrum/gui/qml/qenetwork.py

@ -86,6 +86,10 @@ class QENetwork(QObject, QtEventListener):
self.proxySet.emit()
self.proxyTorChanged.emit()
@event_listener
def on_event_tor_probed(self, *args):
self.proxyTorChanged.emit()
def _update_status(self):
server = str(self.network.get_parameters().server)
if self._server != server:
@ -263,7 +267,7 @@ class QENetwork(QObject, QtEventListener):
proxyTorChanged = pyqtSignal()
@pyqtProperty(bool, notify=proxyTorChanged)
def isProxyTor(self):
return self.network.tor_proxy
return bool(self.network.is_proxy_tor)
@pyqtProperty('QVariant', notify=feeHistogramUpdated)
def feeHistogram(self):

16
electrum/gui/qt/main_window.py

@ -496,6 +496,14 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
def on_event_cert_mismatch(self, *args):
self.show_cert_mismatch_error()
@qt_event_listener
def on_event_tor_probed(self, is_tor):
self.tor_button.setVisible(is_tor)
@qt_event_listener
def on_event_proxy_set(self, *args):
self.tor_button.setVisible(False)
def close_wallet(self):
if self.wallet:
self.logger.info(f'close_wallet {self.wallet.storage.path}')
@ -956,6 +964,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
network_text = ""
balance_text = ""
if self.tor_button:
self.tor_button.setVisible(self.network and bool(self.network.is_proxy_tor))
if self.network is None:
network_text = _("Offline")
icon = read_QIcon("status_disconnected.png")
@ -1634,9 +1645,14 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
sb.addPermanentWidget(self.lightning_button)
self.update_lightning_icon()
self.status_button = None
self.tor_button = None
if self.network:
self.status_button = StatusBarButton(read_QIcon("status_disconnected.png"), _("Network"), self.gui_object.show_network_dialog, sb_height)
sb.addPermanentWidget(self.status_button)
self.tor_button = StatusBarButton(read_QIcon("tor_logo.png"), _("TOR"),
self.gui_object.show_network_dialog, sb_height)
sb.addPermanentWidget(self.tor_button)
self.tor_button.setVisible(False)
run_hook('create_status_bar', sb)
self.setStatusBar(sb)

2
electrum/gui/qt/network_dialog.py

@ -146,7 +146,7 @@ class NodesListWidget(QTreeWidget):
def update(self, *, network: Network, servers: dict):
self.clear()
use_tor = network.tor_proxy
use_tor = bool(network.is_proxy_tor)
# connected servers
connected_servers_item = QTreeWidgetItem([_("Connected nodes"), ''])

32
electrum/network.py

@ -323,6 +323,8 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
self._allowed_protocols = {PREFERRED_NETWORK_PROTOCOL}
self.proxy = None
self.is_proxy_tor = None
self._init_parameters_from_config()
self.taskgroup = None
@ -513,6 +515,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
oneserver=self.oneserver)
def _init_parameters_from_config(self) -> None:
dns_hacks.configure_dns_resolver()
self.auto_connect = self.config.NETWORK_AUTO_CONNECT
self._set_default_server()
self._set_proxy(deserialize_proxy(self.config.NETWORK_PROXY, self.config.NETWORK_PROXY_USER,
@ -628,18 +631,29 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
assert isinstance(self.default_server, ServerAddr), f"invalid type for default_server: {self.default_server!r}"
def _set_proxy(self, proxy: Optional[dict]):
self.proxy = proxy
dns_hacks.configure_dns_resolver()
if self.proxy == proxy:
return
self.logger.info(f'setting proxy {proxy}')
self.proxy = proxy
self.is_proxy_tor = False
def tor_probe_task(p):
assert p is not None
tor_proxy = util.is_tor_socks_port(p['host'], int(p['port']))
if self.proxy == p: # is this the proxy we probed?
self.logger.info(f'Proxy is {"" if tor_proxy else "not "}TOR')
self._tor_probe_done(tor_proxy)
if proxy['mode'] == 'socks5':
t = threading.Thread(target=tor_probe_task, args=(proxy,), daemon=True)
t.start()
self.tor_proxy = False
if bool(proxy) and proxy['mode'] == 'socks5':
# test for Tor
self.tor_proxy = util.is_tor_socks_port(proxy['host'], int(proxy['port']))
if self.tor_proxy:
self.logger.info(f'Proxy is TOR')
util.trigger_callback('proxy_set', self.proxy)
util.trigger_callback('proxy_set', self.proxy, self.tor_proxy)
def _tor_probe_done(self, is_tor: bool):
self.is_proxy_tor = is_tor
util.trigger_callback('tor_probed', is_tor)
@log_exceptions
async def set_parameters(self, net_params: NetworkParameters):

4
electrum/util.py

@ -1506,9 +1506,7 @@ def detect_tor_socks_proxy() -> Optional[Tuple[str, int]]:
def is_tor_socks_port(host: str, port: int) -> bool:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(1.0)
s.connect((host, port))
with socket.create_connection((host, port), timeout=10) as s:
# mimic "tor-resolve 0.0.0.0".
# see https://github.com/spesmilo/electrum/issues/7317#issuecomment-1369281075
# > this is a socks5 handshake, followed by a socks RESOLVE request as defined in

Loading…
Cancel
Save