Integrate Simplified Privacy essentials library

This commit is contained in:
codeking 2026-03-16 15:36:27 +01:00
parent 0ab1ac4168
commit a3a9fb8843
11 changed files with 25 additions and 176 deletions

View file

@ -11,7 +11,6 @@ class Constants:
CONNECTION_RETRY_INTERVAL: Final[int] = int(os.environ.get('CONNECTION_RETRY_INTERVAL', '5')) CONNECTION_RETRY_INTERVAL: Final[int] = int(os.environ.get('CONNECTION_RETRY_INTERVAL', '5'))
MAX_CONNECTION_ATTEMPTS: Final[int] = int(os.environ.get('MAX_CONNECTION_ATTEMPTS', '2')) MAX_CONNECTION_ATTEMPTS: Final[int] = int(os.environ.get('MAX_CONNECTION_ATTEMPTS', '2'))
TOR_BOOTSTRAP_TIMEOUT: Final[int] = int(os.environ.get('TOR_BOOTSTRAP_TIMEOUT', '90'))
HV_CLIENT_PATH: Final[str] = os.environ.get('HV_CLIENT_PATH') HV_CLIENT_PATH: Final[str] = os.environ.get('HV_CLIENT_PATH')
HV_CLIENT_VERSION_NUMBER: Final[str] = os.environ.get('HV_CLIENT_VERSION_NUMBER') HV_CLIENT_VERSION_NUMBER: Final[str] = os.environ.get('HV_CLIENT_VERSION_NUMBER')
@ -48,7 +47,3 @@ class Constants:
HV_SESSION_STATE_HOME: Final[str] = f'{HV_STATE_HOME}/sessions' HV_SESSION_STATE_HOME: Final[str] = f'{HV_STATE_HOME}/sessions'
HV_TOR_STATE_HOME: Final[str] = f'{HV_STATE_HOME}/tor' HV_TOR_STATE_HOME: Final[str] = f'{HV_STATE_HOME}/tor'
HV_TOR_CONTROL_SOCKET_PATH: Final[str] = f'{HV_TOR_STATE_HOME}/tor.sock'
HV_TOR_PROCESS_IDENTIFIER_PATH: Final[str] = f'{HV_TOR_STATE_HOME}/tor.pid'
HV_TOR_INSTANCE_LOCK_PATH: Final[str] = f'{HV_TOR_STATE_HOME}/lock'

View file

@ -26,10 +26,6 @@ class ConnectionTerminationError(Exception):
pass pass
class TorServiceInitializationError(Exception):
pass
class PolicyAssignmentError(Exception): class PolicyAssignmentError(Exception):
pass pass

View file

@ -1,7 +1,6 @@
from collections.abc import Callable from collections.abc import Callable
from concurrent.futures import ThreadPoolExecutor, TimeoutError as FutureTimeoutError
from core.Constants import Constants from core.Constants import Constants
from core.Errors import InvalidSubscriptionError, MissingSubscriptionError, ConnectionUnprotectedError, ConnectionTerminationError, CommandNotFoundError, TorServiceInitializationError from core.Errors import InvalidSubscriptionError, MissingSubscriptionError, ConnectionUnprotectedError, ConnectionTerminationError, CommandNotFoundError
from core.controllers.ConfigurationController import ConfigurationController from core.controllers.ConfigurationController import ConfigurationController
from core.controllers.ProfileController import ProfileController from core.controllers.ProfileController import ProfileController
from core.controllers.SessionStateController import SessionStateController from core.controllers.SessionStateController import SessionStateController
@ -9,20 +8,17 @@ from core.controllers.SystemStateController import SystemStateController
from core.models.session.SessionProfile import SessionProfile from core.models.session.SessionProfile import SessionProfile
from core.models.system.SystemProfile import SystemProfile from core.models.system.SystemProfile import SystemProfile
from core.models.system.SystemState import SystemState from core.models.system.SystemState import SystemState
from core.observers import ConnectionObserver from core.observers.ConnectionObserver import ConnectionObserver
from core.services.WebServiceApiService import WebServiceApiService from core.services.WebServiceApiService import WebServiceApiService
from essentials.modules.TorModule import TorModule
from pathlib import Path from pathlib import Path
from subprocess import CalledProcessError from subprocess import CalledProcessError
from typing import Union, Optional, Any from typing import Union, Optional, Any
import os import os
import psutil
import random import random
import re import re
import shutil import shutil
import socket import socket
import stem
import stem.control
import stem.process
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
@ -209,123 +205,34 @@ class ConnectionController:
ConnectionController.terminate_tor_connection() ConnectionController.terminate_tor_connection()
time.sleep(1.0) time.sleep(1.0)
@staticmethod
def establish_tor_session_connection(port_number: int, connection_observer: Optional[ConnectionObserver] = None):
try:
controller = stem.control.Controller.from_socket_file(Constants.HV_TOR_CONTROL_SOCKET_PATH)
controller.authenticate()
except (FileNotFoundError, stem.SocketError, TypeError, IndexError):
ConnectionController.establish_tor_connection(connection_observer=connection_observer)
controller = stem.control.Controller.from_socket_file(Constants.HV_TOR_CONTROL_SOCKET_PATH)
controller.authenticate()
socks_port_numbers = [str(port_number) for port_number in controller.get_ports('socks')]
socks_port_numbers.append(str(port_number))
controller.set_conf('SocksPort', socks_port_numbers)
@staticmethod
def terminate_tor_session_connection(port_number: int):
try:
controller = stem.control.Controller.from_socket_file(Constants.HV_TOR_CONTROL_SOCKET_PATH)
controller.authenticate()
socks_port_numbers = [str(port_number) for port_number in controller.get_ports('socks')]
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]
controller.set_conf('SocksPort', socks_port_numbers)
else:
controller.set_conf('SocksPort', '0')
except (FileNotFoundError, stem.SocketError, TypeError, IndexError):
pass
@staticmethod @staticmethod
def establish_tor_connection(connection_observer: Optional[ConnectionObserver] = None): def establish_tor_connection(connection_observer: Optional[ConnectionObserver] = None):
Path(Constants.HV_TOR_STATE_HOME).mkdir(mode=0o700, parents=True, exist_ok=True) tor_module = TorModule(Constants.HV_TOR_STATE_HOME)
tor_module.start_service(connection_observer)
ConnectionController.terminate_tor_connection()
if connection_observer is not None:
connection_observer.notify('tor_bootstrapping')
with ThreadPoolExecutor(max_workers=1) as executor:
future = executor.submit(
stem.process.launch_tor_with_config,
config={
'DataDirectory': Constants.HV_TOR_STATE_HOME,
'ControlSocket': Constants.HV_TOR_CONTROL_SOCKET_PATH,
'PIDFile': Constants.HV_TOR_PROCESS_IDENTIFIER_PATH,
'SocksPort': '0'
},
init_msg_handler=lambda contents: ConnectionController.__on_tor_initialization_message(contents, connection_observer)
)
try:
future.result(timeout=Constants.TOR_BOOTSTRAP_TIMEOUT)
except FutureTimeoutError:
ConnectionController.terminate_tor_connection()
raise TorServiceInitializationError('The dedicated Tor service could not be initialized.')
if connection_observer is not None:
connection_observer.notify('tor_bootstrapped')
try:
controller = stem.control.Controller.from_socket_file(Constants.HV_TOR_CONTROL_SOCKET_PATH)
controller.authenticate()
except (FileNotFoundError, stem.SocketError, TypeError, IndexError):
ConnectionController.terminate_tor_connection()
raise TorServiceInitializationError('The dedicated Tor service could not be initialized.')
for session_state in SessionStateController.all(): for session_state in SessionStateController.all():
for port_number in session_state.network_port_numbers.tor: for port_number in session_state.network_port_numbers.tor:
ConnectionController.establish_tor_session_connection(port_number) tor_module.create_session(port_number)
@staticmethod @staticmethod
def terminate_tor_connection(): def terminate_tor_connection():
control_socket_file = Path(Constants.HV_TOR_CONTROL_SOCKET_PATH) tor_module = TorModule(Constants.HV_TOR_STATE_HOME)
process_identifier_file = Path(Constants.HV_TOR_PROCESS_IDENTIFIER_PATH) tor_module.stop_service()
instance_lock_file = Path(Constants.HV_TOR_INSTANCE_LOCK_PATH)
try: @staticmethod
process_identifier = int(process_identifier_file.read_text().strip()) def establish_tor_session_connection(port_number: int, connection_observer: Optional[ConnectionObserver] = None):
except (OSError, ValueError):
process_identifier = None
if process_identifier is not None: tor_module = TorModule(Constants.HV_TOR_STATE_HOME)
tor_module.create_session(port_number, connection_observer)
try: @staticmethod
def terminate_tor_session_connection(port_number: int):
process = psutil.Process(process_identifier) tor_module = TorModule(Constants.HV_TOR_STATE_HOME)
tor_module.destroy_session(port_number)
if process.is_running():
process.terminate()
except psutil.NoSuchProcess:
pass
control_socket_file.unlink(missing_ok=True)
process_identifier_file.unlink(missing_ok=True)
instance_lock_file.unlink(missing_ok=True)
@staticmethod @staticmethod
def establish_wireguard_session_connection(profile: SessionProfile, session_directory: str, port_number: int): def establish_wireguard_session_connection(profile: SessionProfile, session_directory: str, port_number: int):
@ -586,16 +493,3 @@ class ConnectionController:
return True return True
return False return False
@staticmethod
def __on_tor_initialization_message(contents, connection_observer: Optional[ConnectionObserver] = None):
if connection_observer is not None:
if 'Bootstrapped ' in contents:
progress = (m := re.search(r' (\d{1,3})% ', contents)) and int(m.group(1))
connection_observer.notify('tor_bootstrap_progressing', None, dict(
progress=progress
))

View file

@ -1,6 +0,0 @@
class Event:
def __init__(self, subject = None, meta = None):
self.subject = subject
self.meta = meta or {}

View file

@ -1,4 +1,4 @@
from core.observers.BaseObserver import BaseObserver from essentials.observers.BaseObserver import BaseObserver
class ApplicationVersionObserver(BaseObserver): class ApplicationVersionObserver(BaseObserver):

View file

@ -1,25 +0,0 @@
from core.models.Event import Event
class BaseObserver:
def subscribe(self, topic, callback):
callbacks = getattr(self, f'on_{topic}', None)
if callbacks is None:
return
callbacks.append(callback)
def notify(self, topic, subject = None, meta = None):
callbacks = getattr(self, f'on_{topic}', None)
if callbacks is None:
return
event = Event(subject, meta)
for callback in callbacks:
callback(event)

View file

@ -1,4 +1,4 @@
from core.observers.BaseObserver import BaseObserver from essentials.observers.BaseObserver import BaseObserver
class ClientObserver(BaseObserver): class ClientObserver(BaseObserver):

View file

@ -1,10 +1,5 @@
from core.observers.BaseObserver import BaseObserver from essentials.observers.ConnectionObserver import ConnectionObserver as BaseConnectionObserver
class ConnectionObserver(BaseObserver): class ConnectionObserver(BaseConnectionObserver):
pass
def __init__(self):
self.on_connecting = []
self.on_tor_bootstrapping = []
self.on_tor_bootstrap_progressing = []
self.on_tor_bootstrapped = []

View file

@ -1,4 +1,4 @@
from core.observers.BaseObserver import BaseObserver from essentials.observers.BaseObserver import BaseObserver
class InvoiceObserver(BaseObserver): class InvoiceObserver(BaseObserver):

View file

@ -1,4 +1,4 @@
from core.observers.BaseObserver import BaseObserver from essentials.observers.BaseObserver import BaseObserver
class ProfileObserver(BaseObserver): class ProfileObserver(BaseObserver):

View file

@ -2,7 +2,7 @@
name = "sp-hydra-veil-core" name = "sp-hydra-veil-core"
version = "2.2.1" version = "2.2.1"
authors = [ authors = [
{ name = "Simplified Privacy" }, { name = "Simplified Privacy" },
] ]
description = "A library that exposes core logic to higher-level components." description = "A library that exposes core logic to higher-level components."
readme = "README.md" readme = "README.md"
@ -19,7 +19,7 @@ dependencies = [
"pysocks ~= 1.7.1", "pysocks ~= 1.7.1",
"python-dateutil ~= 2.9.0.post0", "python-dateutil ~= 2.9.0.post0",
"requests ~= 2.32.5", "requests ~= 2.32.5",
"stem ~= 1.8.2", "sp-essentials ~= 1.0.0",
] ]
[project.urls] [project.urls]