This commit is contained in:
Andrew Pimenov 2026-10-07 06:57:33 +03:00 • committed by GitHub
commit 02fa94e6c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 41 additions and 31 deletions

View file

@ -71,7 +71,7 @@ from netsleuth.probes.dns_leak import collect_dns_leak
from netsleuth.probes.hop_asn import enrich_hops from netsleuth.probes.hop_asn import enrich_hops
from netsleuth.probes.latency import ping_fanout, tcp_connect_rtt from netsleuth.probes.latency import ping_fanout, tcp_connect_rtt
from netsleuth.probes.nat64 import detect_nat64 from netsleuth.probes.nat64 import detect_nat64
from netsleuth.probes.traceroute import filter_trace_tiers, tier_order, traceroute from netsleuth.probes.traceroute import TracerouteOptions, filter_trace_tiers, tier_order, traceroute
from netsleuth.speed import NDT7_CONSENT_NOTICE from netsleuth.speed import NDT7_CONSENT_NOTICE
if sys.platform == "win32": if sys.platform == "win32":
@ -476,7 +476,8 @@ async def _ecmp_section(caps, settings: Settings, options: Options, cycles: int)
*( *(
traceroute( traceroute(
host, host,
caps, TracerouteOptions(
caps=caps,
max_hops=settings.probing.max_hops, max_hops=settings.probing.max_hops,
cycles=cycles, cycles=cycles,
timeout=settings.timeouts.subprocess_seconds, timeout=settings.timeouts.subprocess_seconds,
@ -484,6 +485,7 @@ async def _ecmp_section(caps, settings: Settings, options: Options, cycles: int)
tcp_trace=options.tcp_trace, tcp_trace=options.tcp_trace,
source_ip=options.bind.ipv4 if options.bind else None, source_ip=options.bind.ipv4 if options.bind else None,
) )
)
for _ in range(cfg.runs) for _ in range(cfg.runs)
) )
) )
@ -498,7 +500,8 @@ async def _traces(hosts, caps, settings: Settings, cycles: int, options: Options
*( *(
traceroute( traceroute(
host, host,
caps, TracerouteOptions(
caps=caps,
max_hops=settings.probing.max_hops, max_hops=settings.probing.max_hops,
cycles=cycles, cycles=cycles,
timeout=settings.timeouts.subprocess_seconds, timeout=settings.timeouts.subprocess_seconds,
@ -506,6 +509,7 @@ async def _traces(hosts, caps, settings: Settings, cycles: int, options: Options
tcp_trace=options.tcp_trace, tcp_trace=options.tcp_trace,
source_ip=options.bind.ipv4 if options.bind else None, source_ip=options.bind.ipv4 if options.bind else None,
) )
)
for _label, host in hosts for _label, host in hosts
) )
) )

View file

@ -7,6 +7,7 @@ import platform
import shutil import shutil
import threading import threading
from collections.abc import Awaitable, Callable from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from typing import TypeVar from typing import TypeVar
from netsleuth.models import Capabilities, TraceConfig, TraceHop, TraceResult from netsleuth.models import Capabilities, TraceConfig, TraceHop, TraceResult
@ -16,6 +17,17 @@ from netsleuth.traceparse import build_trace_result, finalize_hop
_T = TypeVar("_T") _T = TypeVar("_T")
@dataclass(frozen=True)
class TracerouteOptions:
caps: Capabilities
max_hops: int
cycles: int
timeout: float
semaphore: asyncio.Semaphore | None = None
tcp_trace: bool = False
source_ip: str | None = None
def _run_in_daemon_thread(func: Callable[..., _T], *args: object) -> asyncio.Future[_T]: def _run_in_daemon_thread(func: Callable[..., _T], *args: object) -> asyncio.Future[_T]:
# asyncio.to_thread()/loop.run_in_executor(None, ...) queue work onto the # asyncio.to_thread()/loop.run_in_executor(None, ...) queue work onto the
# ThreadPoolExecutor-backed default executor, whose worker threads are non-daemon # ThreadPoolExecutor-backed default executor, whose worker threads are non-daemon
@ -256,29 +268,23 @@ async def _tier_tcp_trace(
async def traceroute( async def traceroute(
target: str, target: str,
caps: Capabilities, options: TracerouteOptions,
max_hops: int,
cycles: int,
timeout: float,
semaphore: asyncio.Semaphore | None = None,
tcp_trace: bool = False,
source_ip: str | None = None,
) -> TraceResult: ) -> TraceResult:
builders = { builders = {
"tcp_trace": lambda: _tier_tcp_trace(target, max_hops, timeout, source_ip=source_ip), "tcp_trace": lambda: _tier_tcp_trace(target, options.max_hops, options.timeout, source_ip=options.source_ip),
"mtr_json": lambda: _tier_mtr( "mtr_json": lambda: _tier_mtr(
target, caps.mtr_binary or shutil.which("mtr") or "mtr", cycles, max_hops, timeout, source_ip target, options.caps.mtr_binary or shutil.which("mtr") or "mtr", options.cycles, options.max_hops, options.timeout, options.source_ip
), ),
"icmp_win": lambda: _tier_icmp_win(target, max_hops, timeout, source_ip), "icmp_win": lambda: _tier_icmp_win(target, options.max_hops, options.timeout, options.source_ip),
"icmplib": lambda: _tier_icmplib(target, max_hops, timeout, source_ip=source_ip), "icmplib": lambda: _tier_icmplib(target, options.max_hops, options.timeout, source_ip=options.source_ip),
"system_traceroute": lambda: _tier_system( "system_traceroute": lambda: _tier_system(
target, caps.traceroute_binary or "traceroute", max_hops, timeout, source_ip target, options.caps.traceroute_binary or "traceroute", options.max_hops, options.timeout, options.source_ip
), ),
} }
order = tier_order(caps, tcp_trace) order = tier_order(options.caps, options.tcp_trace)
order, _dropped = filter_trace_tiers(order, caps.os_name, forced_v4=bool(source_ip)) order, _dropped = filter_trace_tiers(order, options.caps.os_name, forced_v4=bool(options.source_ip))
tiers = [(name, builders[name]) for name in order] tiers = [(name, builders[name]) for name in order]
if semaphore is None: if options.semaphore is None:
return await run_cascade(tiers) return await run_cascade(tiers)
async with semaphore: async with options.semaphore:
return await run_cascade(tiers) return await run_cascade(tiers)