Adjusted Tor Module to work within gui and core's new frameworks. As well as changed the dict observer feedback to string
This commit is contained in:
parent
f913d8a082
commit
fe9a60563b
7 changed files with 98 additions and 44 deletions
|
|
@ -6,8 +6,10 @@ import os
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class Constants:
|
class Constants:
|
||||||
|
|
||||||
TOR_BOOTSTRAP_TIMEOUT: Final[int] = int(os.environ.get('TOR_BOOTSTRAP_TIMEOUT', '90'))
|
TOR_BOOTSTRAP_TIMEOUT: Final[int] = int(
|
||||||
|
os.environ.get("TOR_BOOTSTRAP_TIMEOUT", "90")
|
||||||
|
)
|
||||||
|
|
||||||
TOR_CONTROL_SOCKET_FILENAME: Final[str] = 'tor.sock'
|
TOR_CONTROL_SOCKET_FILENAME: Final[str] = "tor.sock"
|
||||||
TOR_PROCESS_IDENTIFIER_FILENAME: Final[str] = 'tor.pid'
|
TOR_PROCESS_IDENTIFIER_FILENAME: Final[str] = "tor.pid"
|
||||||
TOR_INSTANCE_LOCK_FILENAME: Final[str] = 'lock'
|
TOR_INSTANCE_LOCK_FILENAME: Final[str] = "lock"
|
||||||
|
|
|
||||||
|
|
@ -5,5 +5,6 @@ class CommandNotFoundError(OSError):
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
super().__init__(f"Command '{subject}' could not be found.")
|
super().__init__(f"Command '{subject}' could not be found.")
|
||||||
|
|
||||||
|
|
||||||
class TorServiceInitializationError(Exception):
|
class TorServiceInitializationError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
class Event:
|
class Event:
|
||||||
|
|
||||||
def __init__(self, subject = None, meta = None):
|
def __init__(self, subject=None, meta=None):
|
||||||
|
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
self.meta = meta or {}
|
self.meta = meta or {}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ from concurrent.futures import ThreadPoolExecutor, TimeoutError as FutureTimeout
|
||||||
from essentials.Constants import Constants
|
from essentials.Constants import Constants
|
||||||
from essentials.Errors import TorServiceInitializationError
|
from essentials.Errors import TorServiceInitializationError
|
||||||
from essentials.observers.ConnectionObserver import ConnectionObserver
|
from essentials.observers.ConnectionObserver import ConnectionObserver
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import os
|
import os
|
||||||
|
|
@ -20,30 +21,40 @@ class TorModule:
|
||||||
self.state_home = state_home
|
self.state_home = state_home
|
||||||
self.bootstrap_timeout = Constants.TOR_BOOTSTRAP_TIMEOUT
|
self.bootstrap_timeout = Constants.TOR_BOOTSTRAP_TIMEOUT
|
||||||
|
|
||||||
self.control_socket_path = f'{self.state_home}/{Constants.TOR_CONTROL_SOCKET_FILENAME}'
|
self.control_socket_path = (
|
||||||
self.process_identifier_path = f'{self.state_home}/{Constants.TOR_PROCESS_IDENTIFIER_FILENAME}'
|
f"{self.state_home}/{Constants.TOR_CONTROL_SOCKET_FILENAME}"
|
||||||
self.instance_lock_path = f'{self.state_home}/{Constants.TOR_INSTANCE_LOCK_FILENAME}'
|
)
|
||||||
|
self.process_identifier_path = (
|
||||||
|
f"{self.state_home}/{Constants.TOR_PROCESS_IDENTIFIER_FILENAME}"
|
||||||
|
)
|
||||||
|
self.instance_lock_path = (
|
||||||
|
f"{self.state_home}/{Constants.TOR_INSTANCE_LOCK_FILENAME}"
|
||||||
|
)
|
||||||
|
|
||||||
def start_service(self, connection_observer: Optional[ConnectionObserver] = None):
|
def start_service(self, connection_observer: Optional[ConnectionObserver] = None):
|
||||||
|
|
||||||
|
print("started tor service")
|
||||||
|
|
||||||
Path(self.state_home).mkdir(mode=0o700, parents=True, exist_ok=True)
|
Path(self.state_home).mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||||
|
|
||||||
self.stop_service()
|
self.stop_service()
|
||||||
|
|
||||||
if connection_observer is not None:
|
# if connection_observer is not None:
|
||||||
connection_observer.notify('tor_bootstrapping')
|
# connection_observer.notify("tor_bootstrapping")
|
||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=1) as executor:
|
with ThreadPoolExecutor(max_workers=1) as executor:
|
||||||
|
|
||||||
future = executor.submit(
|
future = executor.submit(
|
||||||
stem.process.launch_tor_with_config,
|
stem.process.launch_tor_with_config,
|
||||||
config={
|
config={
|
||||||
'DataDirectory': self.state_home,
|
"DataDirectory": self.state_home,
|
||||||
'ControlSocket': self.control_socket_path,
|
"ControlSocket": self.control_socket_path,
|
||||||
'PIDFile': self.process_identifier_path,
|
"PIDFile": self.process_identifier_path,
|
||||||
'SocksPort': '0'
|
"SocksPort": "0",
|
||||||
},
|
},
|
||||||
init_msg_handler=lambda contents: TorModule.__on_initialization_message(contents, connection_observer)
|
init_msg_handler=lambda contents: TorModule.__on_initialization_message(
|
||||||
|
contents, connection_observer
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -52,23 +63,28 @@ class TorModule:
|
||||||
except FutureTimeoutError:
|
except FutureTimeoutError:
|
||||||
|
|
||||||
self.stop_service()
|
self.stop_service()
|
||||||
raise TorServiceInitializationError('The dedicated Tor service could not be initialized.')
|
raise TorServiceInitializationError(
|
||||||
|
"The dedicated Tor service could not be initialized."
|
||||||
if connection_observer is not None:
|
)
|
||||||
connection_observer.notify('tor_bootstrapped')
|
|
||||||
|
|
||||||
|
# if connection_observer is not None:
|
||||||
|
# connection_observer.notify("tor_bootstrapped")
|
||||||
|
print("tor bootstrapped")
|
||||||
try:
|
try:
|
||||||
|
|
||||||
controller = stem.control.Controller.from_socket_file(self.control_socket_path)
|
controller = stem.control.Controller.from_socket_file(
|
||||||
|
self.control_socket_path
|
||||||
|
)
|
||||||
controller.authenticate()
|
controller.authenticate()
|
||||||
|
|
||||||
except (FileNotFoundError, stem.SocketError, TypeError, IndexError):
|
except (FileNotFoundError, stem.SocketError, TypeError, IndexError):
|
||||||
|
|
||||||
self.stop_service()
|
self.stop_service()
|
||||||
raise TorServiceInitializationError('The dedicated Tor service could not be initialized.')
|
raise TorServiceInitializationError(
|
||||||
|
"The dedicated Tor service could not be initialized."
|
||||||
|
)
|
||||||
|
|
||||||
def stop_service(self):
|
def stop_service(self):
|
||||||
|
|
||||||
control_socket_file = Path(self.control_socket_path)
|
control_socket_file = Path(self.control_socket_path)
|
||||||
process_identifier_file = Path(self.process_identifier_path)
|
process_identifier_file = Path(self.process_identifier_path)
|
||||||
instance_lock_file = Path(self.instance_lock_path)
|
instance_lock_file = Path(self.instance_lock_path)
|
||||||
|
|
@ -88,6 +104,8 @@ class TorModule:
|
||||||
except ProcessLookupError:
|
except ProcessLookupError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
process = psutil.Process(process_identifier)
|
process = psutil.Process(process_identifier)
|
||||||
|
|
||||||
if process.is_running():
|
if process.is_running():
|
||||||
|
|
@ -100,54 +118,87 @@ class TorModule:
|
||||||
process_identifier_file.unlink(missing_ok=True)
|
process_identifier_file.unlink(missing_ok=True)
|
||||||
instance_lock_file.unlink(missing_ok=True)
|
instance_lock_file.unlink(missing_ok=True)
|
||||||
|
|
||||||
def create_session(self, port_number: int, connection_observer: Optional[ConnectionObserver] = None):
|
def create_session(
|
||||||
|
self, port_number: int, connection_observer: Optional[ConnectionObserver] = None
|
||||||
|
):
|
||||||
|
print("created a session")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
controller = stem.control.Controller.from_socket_file(self.control_socket_path)
|
controller = stem.control.Controller.from_socket_file(
|
||||||
|
self.control_socket_path
|
||||||
|
)
|
||||||
controller.authenticate()
|
controller.authenticate()
|
||||||
|
|
||||||
except (FileNotFoundError, stem.SocketError, TypeError, IndexError):
|
except (FileNotFoundError, stem.SocketError, TypeError, IndexError):
|
||||||
|
|
||||||
self.start_service(connection_observer=connection_observer)
|
self.start_service(connection_observer=connection_observer)
|
||||||
|
|
||||||
controller = stem.control.Controller.from_socket_file(self.control_socket_path)
|
controller = stem.control.Controller.from_socket_file(
|
||||||
|
self.control_socket_path
|
||||||
|
)
|
||||||
controller.authenticate()
|
controller.authenticate()
|
||||||
|
|
||||||
socks_port_numbers = [str(port_number) for port_number in controller.get_ports('socks')]
|
socks_port_numbers = [
|
||||||
|
str(port_number) for port_number in controller.get_ports("socks")
|
||||||
|
]
|
||||||
socks_port_numbers.append(str(port_number))
|
socks_port_numbers.append(str(port_number))
|
||||||
|
|
||||||
controller.set_conf('SocksPort', socks_port_numbers)
|
controller.set_conf("SocksPort", socks_port_numbers)
|
||||||
|
|
||||||
def destroy_session(self, port_number: int):
|
def destroy_session(self, port_number: int):
|
||||||
|
print("destroying session")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
controller = stem.control.Controller.from_socket_file(self.control_socket_path)
|
controller = stem.control.Controller.from_socket_file(
|
||||||
|
self.control_socket_path
|
||||||
|
)
|
||||||
controller.authenticate()
|
controller.authenticate()
|
||||||
|
|
||||||
socks_port_numbers = [str(port_number) for port_number in controller.get_ports('socks')]
|
socks_port_numbers = [
|
||||||
|
str(port_number) for port_number in controller.get_ports("socks")
|
||||||
|
]
|
||||||
|
|
||||||
if len(socks_port_numbers) > 1:
|
if len(socks_port_numbers) > 1:
|
||||||
|
|
||||||
socks_port_numbers = [socks_port_number for socks_port_number in socks_port_numbers if socks_port_number != port_number]
|
socks_port_numbers = [
|
||||||
controller.set_conf('SocksPort', socks_port_numbers)
|
socks_port_number
|
||||||
|
for socks_port_number in socks_port_numbers
|
||||||
|
if socks_port_number != port_number
|
||||||
|
]
|
||||||
|
controller.set_conf("SocksPort", socks_port_numbers)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
controller.set_conf('SocksPort', '0')
|
controller.set_conf("SocksPort", "0")
|
||||||
|
|
||||||
except (FileNotFoundError, stem.SocketError, TypeError, IndexError):
|
except (FileNotFoundError, stem.SocketError, TypeError, IndexError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __on_initialization_message(contents, connection_observer: Optional[ConnectionObserver] = None):
|
def __on_initialization_message(
|
||||||
|
contents, connection_observer: Optional[ConnectionObserver] = None
|
||||||
|
):
|
||||||
if connection_observer is not None:
|
if connection_observer is not None:
|
||||||
|
|
||||||
if 'Bootstrapped ' in contents:
|
if "Bootstrapped " in contents:
|
||||||
|
|
||||||
progress = (m := re.search(r' (\d{1,3})% ', contents)) and int(m.group(1))
|
# original version,
|
||||||
|
# progress = (m := re.search(r" (\d{1,3})% ", contents)) and int(
|
||||||
|
# m.group(1)
|
||||||
|
# )
|
||||||
|
|
||||||
|
# # Search for the pattern in the contents
|
||||||
|
match = re.search(r" (\d{1,3})% ", contents)
|
||||||
|
|
||||||
|
# # Check if we found a match
|
||||||
|
if match:
|
||||||
|
# If we found it, get the number and convert to integer
|
||||||
|
progress = int(match.group(1))
|
||||||
|
else:
|
||||||
|
# If we didn't find it, use a default value
|
||||||
|
progress = 0
|
||||||
|
|
||||||
|
progress_string = f"Tor Bootstrap {progress}%"
|
||||||
|
connection_observer.notify("tor_bootstrap_progressing", progress_string)
|
||||||
|
|
||||||
connection_observer.notify('tor_bootstrap_progressing', None, dict(
|
|
||||||
progress=progress
|
|
||||||
))
|
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,16 @@ class BaseObserver:
|
||||||
|
|
||||||
def subscribe(self, topic, callback):
|
def subscribe(self, topic, callback):
|
||||||
|
|
||||||
callbacks = getattr(self, f'on_{topic}', None)
|
callbacks = getattr(self, f"on_{topic}", None)
|
||||||
|
|
||||||
if callbacks is None:
|
if callbacks is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
callbacks.append(callback)
|
callbacks.append(callback)
|
||||||
|
|
||||||
def notify(self, topic, subject = None, meta = None):
|
def notify(self, topic, subject=None, meta=None):
|
||||||
|
|
||||||
callbacks = getattr(self, f'on_{topic}', None)
|
callbacks = getattr(self, f"on_{topic}", None)
|
||||||
|
|
||||||
if callbacks is None:
|
if callbacks is None:
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ class ConnectionService:
|
||||||
def get_random_available_port_number():
|
def get_random_available_port_number():
|
||||||
|
|
||||||
socket_instance = socket.socket()
|
socket_instance = socket.socket()
|
||||||
socket_instance.bind(('', 0))
|
socket_instance.bind(("", 0))
|
||||||
port_number = socket_instance.getsockname()[1]
|
port_number = socket_instance.getsockname()[1]
|
||||||
socket_instance.close()
|
socket_instance.close()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[project]
|
[project]
|
||||||
name = "sp-essentials"
|
name = "sp-essentials"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
authors = [
|
authors = [
|
||||||
{ name = "Simplified Privacy" },
|
{ name = "Simplified Privacy" },
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue