Direct httpx2 IP address checking, instead of subprocess for connection tests.
This commit is contained in:
parent
a7b733db0d
commit
ed517f4568
6 changed files with 102 additions and 25 deletions
|
|
@ -5,7 +5,8 @@ from core.services.networking.general_connection_tools.testing_evaluating import
|
||||||
from core.services.networking.systemwide.systemwide_wireguard import establish_system_connection, terminate_system_connection
|
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 core.services.keys_and_verifications.endpoint_verification import verify_wireguard_endpoint
|
||||||
from core.observers.TicketObserver import TicketObserver
|
from core.observers.TicketObserver import TicketObserver
|
||||||
|
from core.services.networking.systemwide.general_tools.test_any_protocol import test_session_ip
|
||||||
|
from core.models.enum_models import ProtocolChoice
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from core.Constants import Constants
|
from core.Constants import Constants
|
||||||
|
|
@ -79,8 +80,6 @@ class ConnectionController:
|
||||||
else:
|
else:
|
||||||
from core.services.profiles.profile_connections import disable_profile
|
from core.services.profiles.profile_connections import disable_profile
|
||||||
disable_profile(profile=profile, connection_observer=connection_observer, ticket_observer=ticket_observer)
|
disable_profile(profile=profile, connection_observer=connection_observer, ticket_observer=ticket_observer)
|
||||||
# from core.controllers.ProfileController import ProfileController
|
|
||||||
# ProfileController.disable(profile=profile, connection_observer=connection_observer, ticket_observer=ticket_observer)
|
|
||||||
|
|
||||||
|
|
||||||
if profile.connection.code == 'tor':
|
if profile.connection.code == 'tor':
|
||||||
|
|
@ -107,14 +106,28 @@ class ConnectionController:
|
||||||
ConnectionController.establish_proxy_session_connection(profile, session_directory, port_number, proxy_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)
|
session_state.network_port_numbers.proxy.append(proxy_port_number)
|
||||||
|
|
||||||
if not profile.connection.is_unprotected():
|
# if not profile.connection.is_unprotected():
|
||||||
|
|
||||||
|
if profile.connection.code == 'wireguard':
|
||||||
|
test_result = test_session_ip(port=port_number, profile_id=profile.id, protocol=ProtocolChoice.WIREGUARD, connection_observer=connection_observer)
|
||||||
|
if test_result.valid:
|
||||||
|
logger.info("Connection established.")
|
||||||
|
if connection_observer:
|
||||||
|
connection_observer.notify("connected", "Confirmed")
|
||||||
|
else:
|
||||||
|
logger.error(test_result.message)
|
||||||
|
raise ConnectionError(test_result.message)
|
||||||
|
|
||||||
|
elif profile.connection.code == 'tor':
|
||||||
await_connection(proxy_port_number or port_number, connection_observer=connection_observer)
|
await_connection(proxy_port_number or port_number, connection_observer=connection_observer)
|
||||||
|
|
||||||
|
|
||||||
SessionStateController.update_or_create(session_state)
|
SessionStateController.update_or_create(session_state)
|
||||||
|
|
||||||
return proxy_port_number or port_number
|
return proxy_port_number or port_number
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def establish_tor_connection(connection_observer: Optional[ConnectionObserver] = None):
|
def establish_tor_connection(connection_observer: Optional[ConnectionObserver] = None):
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,36 @@
|
||||||
|
from core.models.BaseProfile import ProfileType
|
||||||
from core.Constants import Constants
|
from core.Constants import Constants
|
||||||
import os
|
from core.errors.logger import logger
|
||||||
|
|
||||||
|
import os
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
def extract_endpoint_ip(profile_id: str, profile_type: Optional[ProfileType] = None) -> str | bool:
|
||||||
|
|
||||||
def extract_endpoint_ip(profile_id: str) -> str | bool:
|
|
||||||
try:
|
|
||||||
profile_path = Constants.HV_PROFILE_CONFIG_HOME + f'/{profile_id}'
|
profile_path = Constants.HV_PROFILE_CONFIG_HOME + f'/{profile_id}'
|
||||||
|
|
||||||
|
if not profile_type: # assumes systemwide:
|
||||||
wg_conf_path = f'{profile_path}/wg.conf.bak'
|
wg_conf_path = f'{profile_path}/wg.conf.bak'
|
||||||
print(f"wg_conf_path is {wg_conf_path}")
|
elif profile_type == ProfileType.SESSION:
|
||||||
if not os.path.exists(wg_conf_path):
|
wg_conf_path = f'{profile_path}/wg.conf'
|
||||||
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
if not os.path.exists(wg_conf_path):
|
||||||
|
logger.error(f"Path doesn't exist: {wg_conf_path}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
with open(wg_conf_path, 'r') as f:
|
with open(wg_conf_path, 'r') as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
for line in content.split('\n'):
|
for line in content.split('\n'):
|
||||||
if line.strip().startswith('Endpoint = '):
|
if line.strip().startswith('Endpoint = '):
|
||||||
endpoint = line.strip().split(' = ')[1]
|
endpoint = line.strip().split(' = ')[1]
|
||||||
return endpoint.split(':')[0]
|
return endpoint.split(':')[0]
|
||||||
|
logger.error("Found no content.")
|
||||||
return None
|
return None
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
logger.error(f"Unable to parse WG config: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def extract_dns(profile_id: str) -> str | bool:
|
def extract_dns(profile_id: str) -> str | bool:
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ from core.services.networking.httpx.classify_response import classify_response
|
||||||
from core.models.Result import Result, ResultError
|
from core.models.Result import Result, ResultError
|
||||||
from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType
|
from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType
|
||||||
|
|
||||||
# errors/loggers/observers
|
|
||||||
from core.Constants import Constants
|
from core.Constants import Constants
|
||||||
from core.errors.logger import logger
|
from core.errors.logger import logger
|
||||||
from essentials.observers.ConnectionObserver import ConnectionObserver
|
from essentials.observers.ConnectionObserver import ConnectionObserver
|
||||||
|
|
@ -13,6 +12,10 @@ from essentials.observers.ConnectionObserver import ConnectionObserver
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
import httpx
|
import httpx
|
||||||
|
from httpx_socks import AsyncProxyTransport
|
||||||
|
import httpx_socks
|
||||||
|
from httpx_socks import ProxyType
|
||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
API_ENDPOINT_FORMATS = {
|
API_ENDPOINT_FORMATS = {
|
||||||
|
|
@ -26,7 +29,7 @@ API_ENDPOINT_FORMATS = {
|
||||||
|
|
||||||
MAX_ATTEMPTS = 3
|
MAX_ATTEMPTS = 3
|
||||||
|
|
||||||
def test(connection_observer: Optional[ConnectionObserver]) -> Result:
|
def test(connection_observer: Optional[ConnectionObserver], port: Optional[int] = None) -> Result:
|
||||||
"""
|
"""
|
||||||
Cycles through 3 possible endpoints,
|
Cycles through 3 possible endpoints,
|
||||||
to attempt to get an IP address for the systemwide connection.
|
to attempt to get an IP address for the systemwide connection.
|
||||||
|
|
@ -35,8 +38,17 @@ def test(connection_observer: Optional[ConnectionObserver]) -> Result:
|
||||||
endpoint_index = 0
|
endpoint_index = 0
|
||||||
endpoint_list = list(API_ENDPOINT_FORMATS.keys())
|
endpoint_list = list(API_ENDPOINT_FORMATS.keys())
|
||||||
|
|
||||||
|
if port:
|
||||||
|
transport = httpx_socks.SyncProxyTransport(
|
||||||
|
proxy_type=ProxyType.SOCKS5,
|
||||||
|
proxy_host="127.0.0.1",
|
||||||
|
proxy_port=port,
|
||||||
|
)
|
||||||
|
_ip_test_client = httpx.Client(transport=transport, http2=True, timeout=6)
|
||||||
|
else:
|
||||||
_ip_test_client = httpx.Client(http2=True, timeout=6)
|
_ip_test_client = httpx.Client(http2=True, timeout=6)
|
||||||
|
|
||||||
|
|
||||||
while attempt < MAX_ATTEMPTS:
|
while attempt < MAX_ATTEMPTS:
|
||||||
endpoint_url = endpoint_list[attempt]
|
endpoint_url = endpoint_list[attempt]
|
||||||
attempt += 1
|
attempt += 1
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
from core.services.networking.general_connection_tools.config_tools import extract_endpoint_ip
|
from core.services.networking.general_connection_tools.config_tools import extract_endpoint_ip
|
||||||
from core.services.networking.general_connection_tools import ip
|
from core.services.networking.general_connection_tools import ip
|
||||||
from essentials.observers.ConnectionObserver import ConnectionObserver
|
from essentials.observers.ConnectionObserver import ConnectionObserver
|
||||||
|
from core.models.BaseProfile import ProfileType
|
||||||
from core.models.enum_models import ProtocolChoice
|
from core.models.enum_models import ProtocolChoice
|
||||||
from core.utils.basic_operations.write_or_read_from_json import get_value_from_json_file
|
from core.utils.basic_operations.write_or_read_from_json import get_value_from_json_file
|
||||||
from core.models.Result import Result, ResultError
|
from core.models.Result import Result, ResultError
|
||||||
|
|
@ -11,6 +12,29 @@ from core.Constants import Constants
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def test_session_ip(port: int, profile_id: int, protocol: ProtocolChoice, connection_observer: ConnectionObserver) -> Result:
|
||||||
|
"""Compare the real vs expected IP address."""
|
||||||
|
|
||||||
|
if protocol == ProtocolChoice.WIREGUARD:
|
||||||
|
expected_ip_address = extract_endpoint_ip(profile_id=profile_id, profile_type=ProfileType.SESSION)
|
||||||
|
else:
|
||||||
|
error_msg = "Not currently supported protocol for test_session_ip"
|
||||||
|
logger.error(error_msg)
|
||||||
|
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
|
||||||
|
|
||||||
|
if not expected_ip_address:
|
||||||
|
error_msg = "Could not get the expected IP address from local files."
|
||||||
|
logger.error(error_msg)
|
||||||
|
return Result(valid=False, error_type=ResultError.MISSING_DATA, message=error_msg)
|
||||||
|
|
||||||
|
# reality:
|
||||||
|
logger.info(f"testing on port {port}")
|
||||||
|
ip_result = ip.test(connection_observer=connection_observer, port=port)
|
||||||
|
|
||||||
|
return compare_ips(ip_result=ip_result, expected_ip_address=expected_ip_address)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_singbox_ip(profile_id: int) -> str|None:
|
def get_singbox_ip(profile_id: int) -> str|None:
|
||||||
"""Get the IP address for a Singbox based profile based on local disk."""
|
"""Get the IP address for a Singbox based profile based on local disk."""
|
||||||
config_path = f'{Constants.HV_SYSTEM_PROFILE_CONFIG_PATH}/{profile_id}/proxy.json'
|
config_path = f'{Constants.HV_SYSTEM_PROFILE_CONFIG_PATH}/{profile_id}/proxy.json'
|
||||||
|
|
@ -26,9 +50,6 @@ def get_singbox_ip(profile_id: int) -> str|None:
|
||||||
def test_ip_systemwide_for_any_protocol(profile_id: int, protocol: ProtocolChoice, connection_observer: ConnectionObserver) -> Result:
|
def test_ip_systemwide_for_any_protocol(profile_id: int, protocol: ProtocolChoice, connection_observer: ConnectionObserver) -> Result:
|
||||||
"""Compare the real vs expected IP address."""
|
"""Compare the real vs expected IP address."""
|
||||||
|
|
||||||
# protocol = profile.type
|
|
||||||
# profile_id = profile.id
|
|
||||||
|
|
||||||
# what's expected:
|
# what's expected:
|
||||||
if protocol == ProtocolChoice.WIREGUARD:
|
if protocol == ProtocolChoice.WIREGUARD:
|
||||||
expected_ip_address = extract_endpoint_ip(profile_id)
|
expected_ip_address = extract_endpoint_ip(profile_id)
|
||||||
|
|
@ -45,6 +66,12 @@ def test_ip_systemwide_for_any_protocol(profile_id: int, protocol: ProtocolChoic
|
||||||
# reality:
|
# reality:
|
||||||
ip_result = ip.test(connection_observer)
|
ip_result = ip.test(connection_observer)
|
||||||
|
|
||||||
|
return compare_ips(ip_result=ip_result, expected_ip_address=expected_ip_address)
|
||||||
|
|
||||||
|
|
||||||
|
def compare_ips(ip_result: Result, expected_ip_address: str) -> Result:
|
||||||
|
"""Compare the expected to the real IP address."""
|
||||||
|
|
||||||
if not ip_result.valid:
|
if not ip_result.valid:
|
||||||
logger.error(ip_result.message)
|
logger.error(ip_result.message)
|
||||||
return ip_result # result object
|
return ip_result # result object
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ from core.services.keys_and_verifications.endpoint_verification import verify_wi
|
||||||
from core.models.Result import Result, ResultError
|
from core.models.Result import Result, ResultError
|
||||||
from core.services.networking.systemwide.systemwide_utils import extract_wg_interface_name, check_system_prereqs
|
from core.services.networking.systemwide.systemwide_utils import extract_wg_interface_name, check_system_prereqs
|
||||||
from core.observers.Notifications import Notifications, EventType
|
from core.observers.Notifications import Notifications, EventType
|
||||||
|
from core.services.networking.systemwide.general_tools.test_any_protocol import test_ip_systemwide_for_any_protocol
|
||||||
|
|
||||||
|
|
||||||
# from core.services.networking.general_connection_tools.config_tools import extract_endpoint_ip, extract_dns
|
# from core.services.networking.general_connection_tools.config_tools import extract_endpoint_ip, extract_dns
|
||||||
from core.services.networking.systemwide import killswitch
|
from core.services.networking.systemwide import killswitch
|
||||||
|
|
@ -198,13 +200,16 @@ def __establish_system_connection(
|
||||||
|
|
||||||
# ============= TESTING =============
|
# ============= TESTING =============
|
||||||
logger.info("If we made it this far, we have nmcli on, ipv6 sinkhole on, firewall on.")
|
logger.info("If we made it this far, we have nmcli on, ipv6 sinkhole on, firewall on.")
|
||||||
try:
|
|
||||||
if connection_observer:
|
if connection_observer:
|
||||||
connection_observer.notify("connecting", "Testing Connection Health..")
|
connection_observer.notify("connecting", "Testing Connection Health..")
|
||||||
await_connection(connection_observer=connection_observer)
|
|
||||||
|
|
||||||
except ConnectionError:
|
test_result = test_ip_systemwide_for_any_protocol(
|
||||||
raise ConnectionError('The connection could not be established.')
|
profile_id=profile.id,
|
||||||
|
protocol=ProtocolChoice.WIREGUARD,
|
||||||
|
connection_observer=connection_observer
|
||||||
|
)
|
||||||
|
if not test_result.valid:
|
||||||
|
return test_result
|
||||||
|
|
||||||
# ============= UPDATE STATE =============
|
# ============= UPDATE STATE =============
|
||||||
if dns_setting and not did_dns_work:
|
if dns_setting and not did_dns_work:
|
||||||
|
|
@ -212,6 +217,9 @@ def __establish_system_connection(
|
||||||
current_state.dns_set = did_dns_work
|
current_state.dns_set = did_dns_work
|
||||||
SystemStateController.update_or_create(current_state)
|
SystemStateController.update_or_create(current_state)
|
||||||
|
|
||||||
|
if connection_observer:
|
||||||
|
connection_observer.notify("connected", "Confirmed")
|
||||||
|
|
||||||
Notifications.emit(EventType.SYSTEM_ENABLE, profile.id)
|
Notifications.emit(EventType.SYSTEM_ENABLE, profile.id)
|
||||||
return Result(valid=True, data=current_state)
|
return Result(valid=True, data=current_state)
|
||||||
|
|
||||||
|
|
@ -221,6 +229,7 @@ def evaluate_connection_result(connection_result: Result) -> Result:
|
||||||
"""In the future, this will raise other error types or filter replies."""
|
"""In the future, this will raise other error types or filter replies."""
|
||||||
if not connection_result.valid:
|
if not connection_result.valid:
|
||||||
logger.error(f"[SYSTEMWIDE WG] Critical issue, could not establish a connection: {connection_result.message}")
|
logger.error(f"[SYSTEMWIDE WG] Critical issue, could not establish a connection: {connection_result.message}")
|
||||||
|
time.sleep(1)
|
||||||
raise ConnectionError(f"Connection validation failed: {connection_result.message}")
|
raise ConnectionError(f"Connection validation failed: {connection_result.message}")
|
||||||
else:
|
else:
|
||||||
return connection_result
|
return connection_result
|
||||||
|
|
@ -309,5 +318,5 @@ def establish_system_connection(
|
||||||
raise
|
raise
|
||||||
|
|
||||||
terminate_tor_connection()
|
terminate_tor_connection()
|
||||||
time.sleep(1.0)
|
time.sleep(0.3)
|
||||||
return final_result
|
return final_result
|
||||||
|
|
@ -7,6 +7,7 @@ from core.Constants import Constants
|
||||||
from core.models.Result import Result, ResultError
|
from core.models.Result import Result, ResultError
|
||||||
from core.errors.logger import logger
|
from core.errors.logger import logger
|
||||||
|
|
||||||
|
|
||||||
def wipe_ticket_cycle() -> Result:
|
def wipe_ticket_cycle() -> Result:
|
||||||
"""Wipe the entire global ticketing cycle & all associated tickets."""
|
"""Wipe the entire global ticketing cycle & all associated tickets."""
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue