Browse Source

qt network dialog: don't poll Tor socks proxy, but scan on interaction

Polling is introduces spam in Tor logs.
Also, Tor Browser 12.0 apparently has a bug where our polling renders
the socks proxy unusuable after some time.
see https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/41549

Instead of trying to detect a Tor socks proxy every 10 seconds, we now
run detection when the Qt network dialog gets opened, and also when
the user switches to the "Proxy" tab in the dialog.

fixes https://github.com/spesmilo/electrum/issues/7317
master
SomberNight 3 years ago
parent
commit
253150cb36
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 52
      electrum/gui/qt/network_dialog.py
  2. 27
      electrum/util.py

52
electrum/gui/qt/network_dialog.py

@ -40,6 +40,7 @@ from electrum import constants, blockchain, util
from electrum.interface import ServerAddr, PREFERRED_NETWORK_PROTOCOL
from electrum.network import Network
from electrum.logging import get_logger
from electrum.util import detect_tor_socks_proxy
from .util import (Buttons, CloseButton, HelpButton, read_QIcon, char_width_in_lineedit,
PasswordLineEdit)
@ -66,6 +67,11 @@ class NetworkDialog(QDialog, QtEventListener):
self.register_callbacks()
self._cleaned_up = False
def show(self):
super().show()
if td := self.nlayout.td:
td.trigger_rescan()
@qt_event_listener
def on_event_network_updated(self):
self.nlayout.update()
@ -203,10 +209,11 @@ class NetworkChoiceLayout(object):
self.tor_proxy = None
self.tabs = tabs = QTabWidget()
proxy_tab = QWidget()
self._proxy_tab = proxy_tab = QWidget()
blockchain_tab = QWidget()
tabs.addTab(blockchain_tab, _('Overview'))
tabs.addTab(proxy_tab, _('Proxy'))
tabs.currentChanged.connect(self._on_tab_changed)
fixed_width_hostname = 24 * char_width_in_lineedit()
fixed_width_port = 6 * char_width_in_lineedit()
@ -417,6 +424,10 @@ class NetworkChoiceLayout(object):
net_params = net_params._replace(proxy=proxy)
self.network.run_from_another_thread(self.network.set_parameters(net_params))
def _on_tab_changed(self):
if self.tabs.currentWidget() is self._proxy_tab:
self.td.trigger_rescan()
def suggest_proxy(self, found_proxy):
if found_proxy is None:
self.tor_cb.hide()
@ -457,38 +468,25 @@ class TorDetector(QThread):
def __init__(self):
QThread.__init__(self)
self._stop_event = threading.Event()
self._work_to_do_evt = threading.Event()
self._stopping = False
def run(self):
# Probable ports for Tor to listen at
ports = [9050, 9150]
while True:
for p in ports:
net_addr = ("127.0.0.1", p)
if TorDetector.is_tor_port(net_addr):
# do rescan
net_addr = detect_tor_socks_proxy()
self.found_proxy.emit(net_addr)
break
else:
self.found_proxy.emit(None)
stopping = self._stop_event.wait(10)
if stopping:
# wait until triggered
self._work_to_do_evt.wait()
self._work_to_do_evt.clear()
if self._stopping:
return
def trigger_rescan(self) -> None:
self._work_to_do_evt.set()
def stop(self):
self._stop_event.set()
self._stopping = True
self._work_to_do_evt.set()
self.exit()
self.wait()
@staticmethod
def is_tor_port(net_addr: Tuple[str, int]) -> bool:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(0.1)
s.connect(net_addr)
# Tor responds uniquely to HTTP-like requests
s.send(b"GET\n")
if b"Tor is not an HTTP Proxy" in s.recv(1024):
return True
except socket.error:
pass
return False

27
electrum/util.py

@ -47,6 +47,7 @@ import random
import secrets
import functools
from abc import abstractmethod, ABC
import socket
import attr
import aiohttp
@ -1472,6 +1473,32 @@ class NetworkJobOnDefaultServer(Logger, ABC):
return s
def detect_tor_socks_proxy() -> Optional[Tuple[str, int]]:
# Probable ports for Tor to listen at
candidates = [
("127.0.0.1", 9050),
("127.0.0.1", 9150),
]
for net_addr in candidates:
if is_tor_socks_port(*net_addr):
return net_addr
return None
def is_tor_socks_port(host: str, port: int) -> bool:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(0.1)
s.connect((host, port))
# Tor responds uniquely to HTTP-like requests
s.send(b"GET\n")
if b"Tor is not an HTTP Proxy" in s.recv(1024):
return True
except socket.error:
pass
return False
_asyncio_event_loop = None # type: Optional[asyncio.AbstractEventLoop]
def get_asyncio_loop() -> asyncio.AbstractEventLoop:
"""Returns the global asyncio event loop we use."""

Loading…
Cancel
Save