Improved Connection Observer Feedback for all systemwide protocols
This commit is contained in:
parent
8968d89c5c
commit
a69b87817c
4 changed files with 62 additions and 10 deletions
|
|
@ -119,9 +119,14 @@ class ProfileController:
|
|||
# ============================================================================
|
||||
if profile.is_system_profile():
|
||||
try:
|
||||
establish_connection(profile, ignore=ignore, connection_observer=connection_observer)
|
||||
connection_result = establish_connection(profile, ignore=ignore, connection_observer=connection_observer)
|
||||
if connection_result.valid:
|
||||
if profile_observer is not None:
|
||||
profile_observer.notify('enabled', profile)
|
||||
else:
|
||||
logger.error(f"Couldn't enable the profile: {connection_result.error_type}")
|
||||
error_msg = connection_result.message
|
||||
raise ProfileActivationError(error_msg)
|
||||
except FirewallError:
|
||||
raise
|
||||
except ConnectionError:
|
||||
|
|
|
|||
|
|
@ -193,7 +193,15 @@ def _establish_with_renegotiation(
|
|||
except ConnectionError:
|
||||
if __should_renegotiate(profile):
|
||||
logger.info(f"[CONNECTION] Renegotiating a new wg key session with API..")
|
||||
|
||||
# KEY
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", "Renegotiating WG Key..")
|
||||
register_wireguard_session(profile, connection_observer=connection_observer)
|
||||
|
||||
# Reconnect:
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", "Got New WG Key, Connecting..")
|
||||
return establish_fn(profile, ignore=ignore, connection_observer=connection_observer, ticket_observer=ticket_observer)
|
||||
raise ConnectionError('The connection could not be established.')
|
||||
except FirewallError:
|
||||
|
|
|
|||
|
|
@ -238,6 +238,8 @@ def start_singbox(
|
|||
logger.error(f"{error_msg} but that's okay, because {protocol_choice} doesn't use it.")
|
||||
|
||||
# ============= START BINARY =============
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", "Starting Software..")
|
||||
launched = _attempt_start_with_retry(profile_id=profile_id, quantity_of_attempts=2)
|
||||
if not launched.valid:
|
||||
return launched
|
||||
|
|
@ -246,6 +248,8 @@ def start_singbox(
|
|||
logger.info(f"Binary started with process id {process_id}.")
|
||||
|
||||
# ============= CHECK INTERFACE =============
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", "Testing Interface..")
|
||||
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}"
|
||||
|
|
@ -259,8 +263,13 @@ def start_singbox(
|
|||
|
||||
if interface_up:
|
||||
logger.info("Interface is confirmed to be Up")
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", "Interface looking healthy..")
|
||||
else:
|
||||
logger.error("Interface is DOWN")
|
||||
error_msg = "Interface is DOWN"
|
||||
logger.error(error_msg)
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", error_msg)
|
||||
return Result(valid=False, error_type=ResultError.INTERFACE, data=process_id)
|
||||
|
||||
# ======= CONFIRM VIA SINGBOX OUTPUT ==========
|
||||
|
|
@ -268,6 +277,8 @@ def start_singbox(
|
|||
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.")
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", "Software looking healthy so far..")
|
||||
|
||||
# ============= 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.
|
||||
|
|
@ -292,10 +303,15 @@ def start_singbox(
|
|||
observed_ip_address = ip_result.data
|
||||
if observed_ip_address == server_ip:
|
||||
logger.info("Observed IP matches the intended server IP. Proceeding..")
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", f"IP Matched! {server_ip}")
|
||||
else:
|
||||
error_msg = f"Critical Leak! IP address check of {observed_ip_address} does NOT match the intended {server_ip}."
|
||||
error_msg = f"Connection didn't work, it's leaking! IP address check of {observed_ip_address} does NOT match the intended {server_ip}."
|
||||
logger.error(f"{error_msg} Killing Singbox pid {process_id}")
|
||||
closed = orchestrate_closing(current_state=current_state, interface=Constants.SINGBOX_TUN_IF)
|
||||
error_for_user = f"Failed Connection! Test showed leak! Closed Status: {closed}"
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", error_for_user)
|
||||
return Result(valid=False, error_type=ResultError.LEAK_ISSUE, message=error_msg)
|
||||
|
||||
# ============= FIREWALL =============
|
||||
|
|
@ -313,9 +329,13 @@ def start_singbox(
|
|||
current_state.dns_set = False
|
||||
SystemStateController.update_or_create(current_state)
|
||||
raise FirewallError(firewall_result) # Now end the party.
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", "Firewall Enabled.")
|
||||
|
||||
# ============= DNS =============
|
||||
dns_result = set_dns_for_singbox(current_state)
|
||||
if dns_result and connection_observer:
|
||||
connection_observer.notify("connecting", "DNS Changed.")
|
||||
# raises error if not okay.
|
||||
|
||||
# ============= CONCLUSION =============
|
||||
|
|
|
|||
|
|
@ -112,6 +112,8 @@ def __establish_system_connection(
|
|||
terminate_system_connection(firewall_setting, dns_setting)
|
||||
|
||||
# ============= INITIAL NMCLI CONNECTION =============
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", "Setting Up WireGuard..")
|
||||
wg_config_path = profile.get_wireguard_configuration_path()
|
||||
nmcli_result = setup_nmcli_connection(wg_config_path)
|
||||
|
||||
|
|
@ -124,6 +126,8 @@ def __establish_system_connection(
|
|||
raise ConnectionError(f'NMCLI could not be enabled. {nmcli_result.message}')
|
||||
|
||||
# ============= IPv6 SINKHOLE =============
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", "Setting Up IPv6 Protection..")
|
||||
sinkhole_result = setup_ipv6_sinkhole(process_output)
|
||||
if not sinkhole_result:
|
||||
raise ConnectionError('IPv6 Could not be protected.')
|
||||
|
|
@ -139,6 +143,8 @@ def __establish_system_connection(
|
|||
)
|
||||
|
||||
# ============= PREP SETTINGS =============
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", "Getting Interface Name..")
|
||||
if firewall_setting or dns_setting:
|
||||
wg_interface_result = extract_wg_interface_name(process_output)
|
||||
if not wg_interface_result.valid:
|
||||
|
|
@ -154,6 +160,8 @@ def __establish_system_connection(
|
|||
|
||||
if firewall_setting:
|
||||
logger.info("Firewall setting is enabled, attempting to enable...")
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", "Turning Firewall On..")
|
||||
enable_firewall_with_retry(
|
||||
profile_id=str(profile.id),
|
||||
wg_interface_name=wg_interface_name
|
||||
|
|
@ -162,6 +170,8 @@ def __establish_system_connection(
|
|||
|
||||
# ============= DNS =============
|
||||
if dns_setting:
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", "Setting DNS Up..")
|
||||
set_dns_result = set_dns(str(profile.id), wg_interface_name)
|
||||
if not set_dns_result.valid:
|
||||
logger.error(f"[CONNECT] Setting the DNS DIDN'T work. {set_dns_result.message}")
|
||||
|
|
@ -178,6 +188,8 @@ def __establish_system_connection(
|
|||
# ============= TESTING =============
|
||||
logger.info("If we made it this far, we have nmcli on, ipv6 sinkhole on, firewall on.")
|
||||
try:
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", "Testing Connection Health..")
|
||||
await_connection(connection_observer=connection_observer)
|
||||
|
||||
except ConnectionError:
|
||||
|
|
@ -193,11 +205,13 @@ def __establish_system_connection(
|
|||
|
||||
|
||||
|
||||
def evaluate_connection_result(connection_result: Result) -> None:
|
||||
"""Validate the connection result. Raises if invalid."""
|
||||
def evaluate_connection_result(connection_result: Result) -> Result:
|
||||
"""In the future, this will raise other error types or filter replies."""
|
||||
if not connection_result.valid:
|
||||
logger.error(f"[SYSTEMWIDE WG] Critical issue, could not establish a connection: {connection_result.message}")
|
||||
raise ConnectionError(f"Connection validation failed: {connection_result.message}")
|
||||
else:
|
||||
return connection_result
|
||||
|
||||
|
||||
def _cleanup_on_error(firewall_setting: bool, dns_setting: bool) -> None:
|
||||
|
|
@ -213,12 +227,14 @@ def _establish_connection_with_retry(
|
|||
firewall_setting: bool,
|
||||
dns_setting: bool,
|
||||
connection_observer: Optional[ConnectionObserver],
|
||||
max_retries: int = 1
|
||||
max_retries: int = 2
|
||||
) -> Result:
|
||||
"""Establish connection with retry logic for transient errors."""
|
||||
last_error = None
|
||||
|
||||
for attempt in range(1, max_retries + 1):
|
||||
for attempt in range(1, max_retries):
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", f"Connecting attempt {attempt} of {max_retries}.. ")
|
||||
try:
|
||||
connection_result = __establish_system_connection(
|
||||
profile=profile,
|
||||
|
|
@ -259,6 +275,8 @@ def establish_system_connection(
|
|||
3) Retry on failure
|
||||
"""
|
||||
# ================= VERIFY ENDPOINT =================
|
||||
if connection_observer:
|
||||
connection_observer.notify("connecting", "Verifying endpoint..")
|
||||
if ConfigurationController.get_endpoint_verification_enabled():
|
||||
verify_wireguard_endpoint(profile, ignore=ignore)
|
||||
|
||||
|
|
@ -268,7 +286,7 @@ def establish_system_connection(
|
|||
|
||||
# ================= CONNECT WG ================
|
||||
try:
|
||||
_establish_connection_with_retry(
|
||||
final_result = _establish_connection_with_retry(
|
||||
profile=profile,
|
||||
firewall_setting=firewall_setting,
|
||||
dns_setting=dns_setting,
|
||||
|
|
@ -280,3 +298,4 @@ def establish_system_connection(
|
|||
|
||||
terminate_tor_connection()
|
||||
time.sleep(1.0)
|
||||
return final_result
|
||||
Loading…
Reference in a new issue