Singbox on disable, checks if wireguard is active, and kills it

This commit is contained in:
SimplifiedPrivacy 2026-10-15 16:20:44 -04:00
parent 732b17fb4a
commit 5dc1463862
8 changed files with 102 additions and 18 deletions

View file

@ -1,23 +1,24 @@
from core.services.profiles.ProfileRepository import ProfileRepository
from core.services.networking.systemwide.systemwide_wireguard import terminate_system_connection
from core.services.networking.general_connection_tools.testing_evaluating import system_uses_wireguard_interface
from core.services.networking.general_connection_tools.connection_enable import establish_connection
from core.services.networking.systemwide.systemwide_utils import get_firewall_setting, get_dns_setting
from core.services.networking.systemwide.encrypted_proxy.singbox_runner import end_singbox
from core.services.networking.systemwide import killswitch
# from core.services.networking.systemwide.systemwide_wireguard import terminate_system_connection
# from core.services.networking.general_connection_tools.testing_evaluating import system_uses_wireguard_interface
# from core.services.networking.general_connection_tools.connection_enable import establish_connection
# from core.services.networking.systemwide.systemwide_utils import get_firewall_setting, get_dns_setting
# from core.services.networking.systemwide.encrypted_proxy.singbox_runner import end_singbox
# from core.services.networking.systemwide import killswitch
from core.services.subscriptions import subscriptions
from core.errors.exceptions import FirewallError
# from core.errors.exceptions import FirewallError
from core.models.Result import Result, ResultError
from core.observers.TicketObserver import TicketObserver
# from core.observers.TicketObserver import TicketObserver
# from core.services.assassin.ticket_respawn import respawn_profile
# from core.services.assassin import assassin_tools
from core.errors.logger import logger
from core.Errors import InvalidSubscriptionError, MissingSubscriptionError, ConnectionTerminationError, ProfileActivationError, ProfileDeactivationError, MissingLocationError, ConnectionUnprotectedError, EndpointVerificationError, ProfileStateConflictError
from core.controllers.ApplicationController import ApplicationController
from core.controllers.ApplicationVersionController import ApplicationVersionController
from core.controllers.SessionStateController import SessionStateController
from core.controllers.SystemStateController import SystemStateController
from core.Errors import InvalidSubscriptionError, MissingSubscriptionError
#, ConnectionTerminationError, ProfileActivationError, ProfileDeactivationError, MissingLocationError, ConnectionUnprotectedError, EndpointVerificationError, ProfileStateConflictError
# from core.controllers.ApplicationController import ApplicationController
# from core.controllers.ApplicationVersionController import ApplicationVersionController
# from core.controllers.SessionStateController import SessionStateController
# from core.controllers.SystemStateController import SystemStateController
from core.models.BaseProfile import BaseProfile as Profile
from core.models.Subscription import Subscription
from core.models.session.SessionProfile import SessionProfile

View file

@ -0,0 +1,15 @@
from core.models.enum_models import ProtocolChoice, valid_enum
from core.controllers.ProfileController import ProfileController
def get_protocol_enum(profile_id: int) -> ProtocolChoice|bool:
"""Return the Enum for a given Profile's Protocol Choice"""
profile = ProfileController.get(profile_id)
protocol_string = profile.connection.code
if not valid_enum(protocol_string, ProtocolChoice):
return False
else:
return ProtocolChoice(protocol_string)

View file

@ -8,6 +8,8 @@ class ProtocolChoice(Enum):
SOCK5_PROXY = "proxy"
# MASKED_WIREGUARD = "masked_wireguard"
SINGBOX_PROTOCOLS = [ProtocolChoice.HYSTERIA2, ProtocolChoice.VLESS]
class Currency(Enum):
MONERO = "monero"
BITCOIN = "bitcoin"

View file

@ -1,4 +1,7 @@
from core.models.enum_models import ProtocolChoice
from core.Constants import Constants
# generic
from dataclasses import dataclass
from dataclasses_json import dataclass_json
from pathlib import Path
@ -14,6 +17,7 @@ class SystemState:
profile_id: int
firewalled: bool
dns_set: bool
protocol: ProtocolChoice
process_id: Optional[int] = None
def save(self: Self):

View file

@ -92,6 +92,7 @@ def launch_encrypted_proxy(
tries = tries + 1
if exists_already:
logger.info("The config exists already, attempting to get the IP from it...")
server_ip = get_value_from_json_file(filepath=config_path, category="outbounds", key="server")
else:
server_ip = setup_config_and_return_ip(

View file

@ -0,0 +1,29 @@
from core.models.enum_models import ProtocolChoice, SINGBOX_PROTOCOLS
from core.models.system.SystemState import SystemState
from core.utils.type_checker import type_checker
from core.models.Result import Result
from core.errors.logger import logger
@type_checker
def kill_any_protocol(current_state: SystemState, active_protocol: ProtocolChoice) -> Result:
"""Kill any protocol that's currently active"""
logger.info(f"Killing the external protocol {active_protocol}")
firewall = current_state.firewalled
dns_set = current_state.dns_set
if active_protocol == ProtocolChoice.WIREGUARD:
from core.services.networking.systemwide_wireguard import terminate_system_connection
terminate_system_connection(
firewall_setting=firewall,
dns_setting=dns_set
)
return Result(valid=True)
elif active_protocol in SINGBOX_PROTOCOLS:
from core.services.networking.systemwide.encrypted_proxy.singbox_runner import end_singbox
return end_singbox()
else:
logger.error("Not a supported Systemwide Profile.")

View file

@ -1,3 +1,6 @@
from core.controllers.profile_state.get_profile_data import get_protocol_enum
from core.models.enum_models import ProtocolChoice
from core.services.networking.general_connection_tools.cross_protocol import kill_any_protocol
from core.services.networking.systemwide.encrypted_proxy import singbox
from core.services.networking.systemwide.encrypted_proxy.close_singbox import orchestrate_closing
from core.services.networking.systemwide.general_tools import interface_tools
@ -137,6 +140,9 @@ def end_singbox() -> Result:
# get by systemstate:
current_state = SystemState.get()
# ------------------
# KILL ZOMBIE SINGBOX NOT TIED TO PROFILES
# ------------------
if not current_state:
# Make sure there's no singbox id:
singbox_pid = pid_tools.get_pid_by_app_name(exact_app_name="sing-box")
@ -154,7 +160,18 @@ def end_singbox() -> Result:
logger.error(f"Critical Issue! State JSON is blank, but Singbox is still running. {singbox_pid}")
# kill the real singbox
# ------------------
# KILL NON-SINGBOX SUCH AS WIREGUARD
# ------------------
active_protocol = get_protocol_enum(current_state.profile_id)
logger.info(f"State JSON shows there is already an active profile with id {profile_id} and protocol {active_protocol}")
if active_protocol not in SINGBOX_PROTOCOLS:
return kill_external_protocol(current_state=current_state, active_protocol=active_protocol)
# ------------------
# KILL THE "REAL" KNOWN ACTIVE SINGBOX PROFILE
# ------------------
try:
killed_existing = orchestrate_closing(
current_state=current_state,
@ -250,6 +267,7 @@ def start_singbox(
# ============= CHECK INTERFACE =============
if connection_observer:
connection_observer.notify("connecting", "Testing Interface..")
print(f"The Constants.SINGBOX_TUN_IF is {Constants.SINGBOX_TUN_IF}")
interface_result = interface_tools.get_output(Constants.SINGBOX_TUN_IF)
if not interface_result.valid:
error_msg = f"We could not get the interface's output, despite having a process id of {process_id}"

View file

@ -1,3 +1,4 @@
from core.models.enum_models import ProtocolChoice, valid_enum
from core.controllers.ConnectionController import ConnectionController
from core.controllers.ProfileController import ProfileController
from core.services.networking.systemwide.systemwide_wireguard import terminate_system_connection
@ -102,7 +103,7 @@ def enable_session_profile(
profile_observer.notify('enabled', profile)
ApplicationController.launch(application_version, profile, port_number, asynchronous=asynchronous, profile_observer=profile_observer, ticket_observer=ticket_observer, connection_observer=connection_observer)
return Result(valid=True)
return Result(valid=True, data=profile.type)
@ -125,7 +126,7 @@ def enable_profile(
if not ProfileStateConflictError in ignore:
raise ProfileStateConflictError('The profile is already enabled or its session was not properly terminated.')
else:
ProfileController.disable(profile)
disable_profile(profile)
# =========== PRISTINE ============
if pristine:
@ -155,7 +156,7 @@ def enable_profile(
if connection_result.valid:
if profile_observer is not None:
profile_observer.notify('enabled', profile)
return Result(valid=True)
return Result(valid=True, data=profile.type)
else:
logger.error(f"Couldn't enable the profile: {connection_result.error_type}")
error_msg = connection_result.message
@ -312,3 +313,16 @@ def destroy_profile(
profile_observer.notify('destroyed', profile)
return Result(valid=True, message="Deleted Profile")
def get_protocol_enum(profile_id: int) -> ProtocolChoice|bool:
"""Return the Enum for a given Profile's Protocol Choice"""
profile = ProfileController.get(profile_id)
protocol_string = profile.connection.code
if not valid_enum(protocol_string, ProtocolChoice):
return False
else:
return ProtocolChoice(protocol_string)