229 lines
10 KiB
Python
229 lines
10 KiB
Python
from core.errors.exceptions import *
|
|
from core.errors.logger import logger
|
|
|
|
from core.services.networking.general_connection_tools.testing_evaluating import await_connection, system_uses_wireguard_interface, await_network_interface
|
|
from core.services.networking.systemwide.systemwide_wireguard import establish_system_connection, terminate_system_connection
|
|
from core.services.keys_and_verifications.endpoint_verification import verify_wireguard_endpoint
|
|
|
|
|
|
from collections.abc import Callable
|
|
from core.Constants import Constants
|
|
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
|
|
from core.controllers.SystemStateController import SystemStateController
|
|
from core.models.BaseProfile import ProfileType
|
|
from core.models.session.SessionProfile import SessionProfile
|
|
from core.models.system.SystemProfile import SystemProfile
|
|
from core.models.system.SystemState import SystemState
|
|
from core.observers.ConnectionObserver import ConnectionObserver
|
|
from core.services.WebServiceApiService import WebServiceApiService
|
|
from essentials.modules.TorModule import TorModule
|
|
from essentials.services.ConnectionService import ConnectionService
|
|
from pathlib import Path
|
|
from subprocess import CalledProcessError
|
|
from typing import Union, Optional, Any
|
|
import os
|
|
import random
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
import time
|
|
from enum import Enum
|
|
# import sys
|
|
# import re
|
|
|
|
|
|
class ConnectionController:
|
|
|
|
@staticmethod
|
|
def with_preferred_connection(*args, task: Callable[..., Any], connection_observer: Optional[ConnectionObserver] = None, **kwargs):
|
|
"""
|
|
This function does a task with a preferred connection type.
|
|
|
|
It is currently being CALLED UPON with keyword based kwargs only, because any args would execute immediately.
|
|
|
|
However, the *args keeps it open to future uses.
|
|
"""
|
|
|
|
connection = ConfigurationController.get_connection()
|
|
|
|
if connection == 'system':
|
|
return task(*args, **kwargs)
|
|
|
|
elif connection == 'tor':
|
|
return ConnectionController.__with_tor_connection(*args, task=task, connection_observer=connection_observer, **kwargs)
|
|
|
|
else:
|
|
return None
|
|
|
|
|
|
@staticmethod
|
|
def establish_session_connection(profile: SessionProfile, ignore: tuple[type[Exception]] = (), connection_observer: Optional[ConnectionObserver] = None):
|
|
|
|
session_directory = tempfile.mkdtemp(prefix='hv-')
|
|
session_state = SessionStateController.get_or_new(profile.id)
|
|
|
|
port_number = None
|
|
proxy_port_number = None
|
|
|
|
# this is a check from SessionConnection of if there's a systemwide with mask
|
|
if profile.connection.is_unprotected():
|
|
|
|
if not system_uses_wireguard_interface():
|
|
|
|
if not ConnectionUnprotectedError in ignore:
|
|
raise ConnectionUnprotectedError('Connection unprotected while the system is not using a WireGuard interface.')
|
|
else:
|
|
from core.controllers.ProfileController import ProfileController
|
|
ProfileController.disable(profile)
|
|
|
|
if profile.connection.code == 'tor':
|
|
|
|
port_number = ConnectionService.get_random_available_port_number()
|
|
ConnectionController.establish_tor_session_connection(port_number, connection_observer=connection_observer)
|
|
|
|
session_state.network_port_numbers.tor.append(port_number)
|
|
|
|
elif profile.connection.code == 'wireguard':
|
|
|
|
if ConfigurationController.get_endpoint_verification_enabled():
|
|
verify_wireguard_endpoint(profile, ignore=ignore)
|
|
|
|
port_number = ConnectionService.get_random_available_port_number()
|
|
ConnectionController.establish_wireguard_session_connection(profile, session_directory, port_number)
|
|
session_state.network_port_numbers.wireguard.append(port_number)
|
|
|
|
if profile.connection.masked:
|
|
|
|
while proxy_port_number is None or proxy_port_number == port_number:
|
|
proxy_port_number = ConnectionService.get_random_available_port_number()
|
|
|
|
ConnectionController.establish_proxy_session_connection(profile, session_directory, port_number, proxy_port_number)
|
|
session_state.network_port_numbers.proxy.append(proxy_port_number)
|
|
|
|
if not profile.connection.is_unprotected():
|
|
await_connection(proxy_port_number or port_number, connection_observer=connection_observer)
|
|
|
|
SessionStateController.update_or_create(session_state)
|
|
|
|
return proxy_port_number or port_number
|
|
|
|
|
|
@staticmethod
|
|
def establish_tor_connection(connection_observer: Optional[ConnectionObserver] = None):
|
|
try:
|
|
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:
|
|
tor_module.create_session(port_number)
|
|
except:
|
|
if connection_observer is not None:
|
|
connection_observer.notify('custom_message', "Tor Can't Initialize")
|
|
|
|
# @staticmethod
|
|
# def terminate_tor_connection():
|
|
|
|
# tor_module = TorModule(Constants.HV_TOR_STATE_HOME)
|
|
# tor_module.stop_service()
|
|
|
|
@staticmethod
|
|
def establish_tor_session_connection(port_number: int, connection_observer: Optional[ConnectionObserver] = None):
|
|
try:
|
|
tor_module = TorModule(Constants.HV_TOR_STATE_HOME)
|
|
tor_module.create_session(port_number, connection_observer)
|
|
except TorServiceInitializationError as e:
|
|
logger.error(f"TorServiceInitializationError. Tor Can't Start: {e}")
|
|
if connection_observer is not None:
|
|
connection_observer.notify('custom_message', "Tor Can't Initialize")
|
|
except Exception as e:
|
|
logger.error(f"Tor Can't Start: {e}")
|
|
|
|
@staticmethod
|
|
def terminate_tor_session_connection(port_number: int):
|
|
|
|
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):
|
|
|
|
if not profile.has_wireguard_configuration():
|
|
raise FileNotFoundError('No valid WireGuard configuration file detected.')
|
|
|
|
wireguard_session_directory = f'{session_directory}/wireguard'
|
|
Path(wireguard_session_directory).mkdir(exist_ok=True, mode=0o700)
|
|
|
|
wireproxy_configuration_file_path = f'{wireguard_session_directory}/wireproxy.conf'
|
|
Path(wireproxy_configuration_file_path).touch(exist_ok=True, mode=0o600)
|
|
|
|
with open(wireproxy_configuration_file_path, 'w') as wireproxy_configuration_file:
|
|
wireproxy_configuration_file.write(f'WGConfig = {profile.get_wireguard_configuration_path()}\n\n[Socks5]\nBindAddress = 127.0.0.1:{str(port_number)}\n')
|
|
|
|
return subprocess.Popen((f'{Constants.HV_RUNTIME_DATA_HOME}/wireproxy/wireproxy', '-c', wireproxy_configuration_file_path), stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
|
|
|
@staticmethod
|
|
def establish_proxy_session_connection(profile: SessionProfile, session_directory: str, port_number: int, proxy_port_number: int):
|
|
|
|
if shutil.which('proxychains4') is None:
|
|
raise CommandNotFoundError('proxychains4')
|
|
|
|
if shutil.which('microsocks') is None:
|
|
raise CommandNotFoundError('microsocks')
|
|
|
|
if profile.has_proxy_configuration():
|
|
proxy_configuration = profile.get_proxy_configuration()
|
|
else:
|
|
raise FileNotFoundError('No valid proxy configuration file detected.')
|
|
|
|
proxy_session_directory = f'{session_directory}/proxy'
|
|
Path(proxy_session_directory).mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
|
|
proxychains_proxy_list = ''
|
|
|
|
if port_number is not None:
|
|
proxychains_proxy_list = f'socks5 127.0.0.1 {port_number}\n'
|
|
|
|
proxychains_proxy_list = proxychains_proxy_list + f'socks5 {proxy_configuration.ip_address} {proxy_configuration.port_number} {proxy_configuration.username} {proxy_configuration.password}'
|
|
proxychains_template_file_path = f'{Constants.HV_RUNTIME_DATA_HOME}/proxychains.ptpl'
|
|
|
|
with open(proxychains_template_file_path, 'r') as proxychains_template_file:
|
|
|
|
proxychains_configuration_file_path = f'{proxy_session_directory}/proxychains.conf'
|
|
Path(proxychains_configuration_file_path).touch(exist_ok=True, mode=0o600)
|
|
|
|
proxychains_configuration_file_contents = proxychains_template_file.read().format(proxy_list=proxychains_proxy_list)
|
|
|
|
with open(proxychains_configuration_file_path, 'w') as proxychains_configuration_file:
|
|
proxychains_configuration_file.write(proxychains_configuration_file_contents)
|
|
|
|
return subprocess.Popen(('proxychains4', '-f', proxychains_configuration_file_path, 'microsocks', '-p', str(proxy_port_number)), stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
|
|
|
|
|
|
|
# Stays here for Tor based only. Will move with Tor later.
|
|
@staticmethod
|
|
def get_proxies(port_number: int):
|
|
|
|
return dict(
|
|
http=f'socks5h://127.0.0.1:{port_number}',
|
|
https=f'socks5h://127.0.0.1:{port_number}'
|
|
)
|
|
|
|
|
|
@staticmethod
|
|
def __with_tor_connection(*args, task: Callable[..., Any], connection_observer: Optional[ConnectionObserver] = None, **kwargs):
|
|
|
|
port_number = ConnectionService.get_random_available_port_number()
|
|
ConnectionController.establish_tor_session_connection(port_number, connection_observer=connection_observer)
|
|
|
|
await_connection(port_number, connection_observer=connection_observer)
|
|
task_output = task(*args, proxies=ConnectionController.get_proxies(port_number), **kwargs)
|
|
|
|
ConnectionController.terminate_tor_session_connection(port_number)
|
|
|
|
return task_output
|