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'))
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_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_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
class TorServiceInitializationError(Exception):
pass
class PolicyAssignmentError(Exception):
pass

View file

@ -1,7 +1,6 @@
from collections.abc import Callable
from concurrent.futures import ThreadPoolExecutor, TimeoutError as FutureTimeoutError
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.ProfileController import ProfileController
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.system.SystemProfile import SystemProfile
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 essentials.modules.TorModule import TorModule
from pathlib import Path
from subprocess import CalledProcessError
from typing import Union, Optional, Any
import os
import psutil
import random
import re
import shutil
import socket
import stem
import stem.control
import stem.process
import subprocess
import sys
import tempfile
@ -209,123 +205,34 @@ class ConnectionController:
ConnectionController.terminate_tor_connection()
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
def establish_tor_connection(connection_observer: Optional[ConnectionObserver] = None):
Path(Constants.HV_TOR_STATE_HOME).mkdir(mode=0o700, parents=True, exist_ok=True)
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.')
tor_module = TorModule(Constants.HV_TOR_STATE_HOME)
tor_module.start_service(connection_observer)
for session_state in SessionStateController.all():
for port_number in session_state.network_port_numbers.tor:
ConnectionController.establish_tor_session_connection(port_number)
tor_module.create_session(port_number)
@staticmethod
def terminate_tor_connection():
control_socket_file = Path(Constants.HV_TOR_CONTROL_SOCKET_PATH)
process_identifier_file = Path(Constants.HV_TOR_PROCESS_IDENTIFIER_PATH)
instance_lock_file = Path(Constants.HV_TOR_INSTANCE_LOCK_PATH)
tor_module = TorModule(Constants.HV_TOR_STATE_HOME)
tor_module.stop_service()
try:
process_identifier = int(process_identifier_file.read_text().strip())
except (OSError, ValueError):
process_identifier = None
@staticmethod
def establish_tor_session_connection(port_number: int, connection_observer: Optional[ConnectionObserver] = 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)
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)
tor_module = TorModule(Constants.HV_TOR_STATE_HOME)
tor_module.destroy_session(port_number)
@staticmethod
def establish_wireguard_session_connection(profile: SessionProfile, session_directory: str, port_number: int):
@ -586,16 +493,3 @@ class ConnectionController:
return True
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):

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):

View file

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

View file

@ -1,4 +1,4 @@
from core.observers.BaseObserver import BaseObserver
from essentials.observers.BaseObserver import 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):

View file

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