Singbox enable now reads the output file to confirm it's up
This commit is contained in:
parent
bdfb933548
commit
fe19044928
3 changed files with 96 additions and 6 deletions
|
|
@ -97,6 +97,7 @@ def launch_encrypted_proxy(
|
|||
return start_singbox(
|
||||
profile_id=profile.id,
|
||||
server_ip=server_ip,
|
||||
protocol_choice=profile.connection.code,
|
||||
connection_observer=connection_observer
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ from core.services.networking.systemwide import killswitch
|
|||
from core.services.networking.systemwide.wireguard.wg_firewall_dns import revert_dns
|
||||
from core.utils.basic_operations import pid_tools
|
||||
from core.services.networking.general_connection_tools import ip
|
||||
from core.utils.basic_operations.write_string_to_text_file import write_string_to_text_file
|
||||
from core.utils.search_tools import search_for_phrase
|
||||
from core.utils.basic_operations.does_file_exist import does_file_exist
|
||||
|
||||
from core.models.Result import Result, ResultError
|
||||
from core.errors.logger import logger
|
||||
|
|
@ -27,6 +30,24 @@ from typing import Optional
|
|||
QUANTITY_OF_ATTEMPTS = 2
|
||||
|
||||
|
||||
def confirm_proxy_started(protocol_choice: str) -> bool:
|
||||
SINGBOX_OUTPUT = Constants.SINGBOX_OUTPUT
|
||||
|
||||
if not does_file_exist(SINGBOX_OUTPUT):
|
||||
return False
|
||||
|
||||
if protocol_choice == "vless":
|
||||
timeout = 13
|
||||
else:
|
||||
timeout = 7
|
||||
|
||||
return search_for_phrase(
|
||||
phrase="sing-box started",
|
||||
textfile_path=SINGBOX_OUTPUT,
|
||||
timeout=timeout
|
||||
)
|
||||
|
||||
|
||||
def set_dns_for_singbox(current_state: SystemState):
|
||||
do_they_even_use_systemd = dns.is_systemd_enabled()
|
||||
|
||||
|
|
@ -61,8 +82,8 @@ def launch_singbox_binary(profile_id: int) -> Result:
|
|||
|
||||
process_id = int(activation_result.data)
|
||||
|
||||
logger.info(f"Waiting 6 seconds to see if the process id {process_id} is still alive..")
|
||||
time.sleep(6)
|
||||
logger.info(f"Waiting 2 seconds to see if the process id {process_id} is still alive..")
|
||||
time.sleep(2)
|
||||
|
||||
# Evaluate if running by that exact pid:
|
||||
active = pid_tools.is_running(pid=process_id, process_name="sing-box")
|
||||
|
|
@ -175,6 +196,7 @@ def end_singbox() -> Result:
|
|||
def start_singbox(
|
||||
profile_id: int,
|
||||
server_ip: str,
|
||||
protocol_choice: str,
|
||||
connection_observer: Optional[ConnectionObserver] = None
|
||||
) -> Result:
|
||||
"""
|
||||
|
|
@ -185,10 +207,13 @@ def start_singbox(
|
|||
1) Validate inputs
|
||||
2) Start the binary
|
||||
3) Evaluate if the process id is still up.
|
||||
4) If it's up, setup the State JSON
|
||||
5) Turn on Firewall
|
||||
6) Turn on DNS
|
||||
7) Update the State
|
||||
4) Check if the interface is up (before we wait for timeout period for output). This gives immediate feedback.
|
||||
5) For a timeout period, we wait to confirm via singbox's output in a text file.
|
||||
6) If it's up, setup the State JSON
|
||||
7) Test the IP address to match the server via an external API
|
||||
8) Turn on Firewall
|
||||
9) Turn on DNS
|
||||
10) Update the State
|
||||
"""
|
||||
# ============= INPUT VALIDATION =============
|
||||
requirements = [profile_id, server_ip]
|
||||
|
|
@ -203,6 +228,15 @@ def start_singbox(
|
|||
return killed_pre_existing
|
||||
logger.info("Pre-existing process ended.")
|
||||
|
||||
# ========= WIPE PRE-EXISTING LOGS =======
|
||||
wiped = write_string_to_text_file(content_to_write="", file_path=Constants.SINGBOX_OUTPUT)
|
||||
if not wiped:
|
||||
error_msg = f"Critical Error with wiping the log at {Constants.SINGBOX_OUTPUT}."
|
||||
if protocol_choice == "vless":
|
||||
logger.error(f"{error_msg} which vless relies upon to see when the connection started.")
|
||||
return Result(valid=False, error_type=ResultError.FILE_SYSTEM, message=error_msg)
|
||||
logger.error(f"{error_msg} but that's okay, because {protocol_choice} doesn't use it.")
|
||||
|
||||
# ============= START BINARY =============
|
||||
launched = _attempt_start_with_retry(profile_id=profile_id, quantity_of_attempts=2)
|
||||
if not launched.valid:
|
||||
|
|
@ -229,6 +263,12 @@ def start_singbox(
|
|||
logger.error("Interface is DOWN")
|
||||
return Result(valid=False, error_type=ResultError.INTERFACE, data=process_id)
|
||||
|
||||
# ======= CONFIRM VIA SINGBOX OUTPUT ==========
|
||||
its_up = confirm_proxy_started(protocol_choice=protocol_choice)
|
||||
if not its_up:
|
||||
return Result(valid=False, error_type=ResultError.CONNECTION, message="After a timeout period, the singbox output is still not showing the connection having started.")
|
||||
logger.info("Singbox output confirming it's up.")
|
||||
|
||||
# ============= SETUP STATE =============
|
||||
# Even if the connection is dead, firewall is off, we want to save the fact we turned Singbox on, before we raise errors.
|
||||
|
||||
|
|
|
|||
49
core/utils/search_tools.py
Normal file
49
core/utils/search_tools.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import time
|
||||
|
||||
def search_for_phrase(
|
||||
phrase: str,
|
||||
textfile_path: str,
|
||||
timeout: int) -> bool:
|
||||
"""
|
||||
Monitor a text file for a phrase.
|
||||
|
||||
Args:
|
||||
phrase: The phrase to search for
|
||||
textfile_path: Path to the text file to monitor
|
||||
timeout: Maximum time in seconds to search
|
||||
|
||||
Returns:
|
||||
True if phrase is found, False if timeout exceeded
|
||||
"""
|
||||
start_time = time.monotonic()
|
||||
last_position = 0
|
||||
|
||||
while True:
|
||||
# Check if timeout has been exceeded
|
||||
elapsed = time.monotonic() - start_time
|
||||
if elapsed >= timeout:
|
||||
return False
|
||||
|
||||
try:
|
||||
# Open file and read from last known position
|
||||
with open(textfile_path, 'r') as f:
|
||||
f.seek(last_position)
|
||||
new_content = f.read()
|
||||
|
||||
# Check if phrase is in new content
|
||||
if phrase in new_content:
|
||||
return True
|
||||
|
||||
# Update position for next iteration
|
||||
last_position = f.tell()
|
||||
|
||||
except FileNotFoundError:
|
||||
# File doesn't exist yet, continue trying
|
||||
pass
|
||||
except Exception as e:
|
||||
# Handle other file errors gracefully
|
||||
pass
|
||||
|
||||
# Wait before checking again
|
||||
time.sleep(0.2)
|
||||
|
||||
Loading…
Reference in a new issue