From 17bd4eee38fcfb2731214c94237830ad532cbd3d Mon Sep 17 00:00:00 2001 From: Preisthe <574726144@qq.com> Date: Sun, 15 Mar 2026 19:57:50 +0800 Subject: [PATCH] fix: fallback to tcp tunnel when quic connection fails on ios 26 --- .gitignore | 2 ++ driver/connect.py | 30 +++++++++++++++++++++++++++--- init/tunnel.py | 28 ++++++++++++++++++++++------ main.py | 12 +++++++++--- 4 files changed, 60 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 7a605bc..5133521 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .vscode/ __pycache__/ + +.DS_Store \ No newline at end of file diff --git a/driver/connect.py b/driver/connect.py index b4be45d..9a56491 100644 --- a/driver/connect.py +++ b/driver/connect.py @@ -7,11 +7,14 @@ from pymobiledevice3.cli.remote import select_device, RemoteServiceDiscoveryService from pymobiledevice3.cli.remote import start_tunnel from pymobiledevice3.cli.remote import verify_tunnel_imports +from pymobiledevice3.remote.core_device_tunnel_service import TunnelProtocol from pymobiledevice3.services.amfi import AmfiService from pymobiledevice3.exceptions import NoDeviceConnectedError +logger = logging.getLogger(__name__) + def get_usbmux_lockdownclient(): while True: try: @@ -50,6 +53,27 @@ def get_serverrsd(): async def tunnel(rsd: RemoteServiceDiscoveryService, queue: multiprocessing.Queue): - async with start_tunnel(rsd, None) as tunnel_result: - queue.put((tunnel_result.address, tunnel_result.port)) - await tunnel_result.client.wait_closed() + tunnel_errors = [] + protocols = (TunnelProtocol.QUIC, TunnelProtocol.TCP) + + for protocol in protocols: + try: + logger.info("starting %s tunnel", protocol.value) + async with start_tunnel(rsd, None, protocol=protocol) as tunnel_result: + logger.info("tunnel established with %s", protocol.value) + queue.put({ + "status": "ok", + "address": tunnel_result.address, + "port": tunnel_result.port, + "protocol": protocol.value, + }) + await tunnel_result.client.wait_closed() + return + except Exception as exc: + logger.warning("failed to start %s tunnel: %s", protocol.value, exc) + tunnel_errors.append(f"{protocol.value}: {exc!r}") + + raise ConnectionError( + "Unable to establish tunnel. " + f"Tried protocols: {', '.join(tunnel_errors)}" + ) diff --git a/init/tunnel.py b/init/tunnel.py index 5119d90..8747362 100644 --- a/init/tunnel.py +++ b/init/tunnel.py @@ -1,11 +1,18 @@ import asyncio import multiprocessing +from queue import Empty from driver import connect def tunnel_proc(queue: multiprocessing.Queue): - server_rsd = connect.get_serverrsd() - asyncio.run(connect.tunnel(server_rsd, queue)) + try: + server_rsd = connect.get_serverrsd() + asyncio.run(connect.tunnel(server_rsd, queue)) + except Exception as exc: + queue.put({ + "status": "error", + "error": repr(exc), + }) def tunnel(): @@ -13,8 +20,17 @@ def tunnel(): queue = multiprocessing.Queue() process = multiprocessing.Process(target=tunnel_proc, args=(queue,)) process.start() - - # get the address and port of the tunnel - address, port = queue.get() - return process, address, port \ No newline at end of file + while True: + try: + result = queue.get(timeout=1) + except Empty: + if not process.is_alive(): + raise RuntimeError("Tunnel process exited before returning connection info") + continue + + if result.get("status") == "ok": + return process, result["address"], result["port"] + + process.join(timeout=1) + raise RuntimeError(f"Tunnel setup failed: {result.get('error', 'unknown error')}") diff --git a/main.py b/main.py index cb273db..68494ad 100644 --- a/main.py +++ b/main.py @@ -49,8 +49,14 @@ def main(): # start the tunnel in another process logger.info("starting tunnel") original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN) - process, address, port = tunnel.tunnel() - signal.signal(signal.SIGINT, original_sigint_handler) + try: + process, address, port = tunnel.tunnel() + except RuntimeError as exc: + logger.error("failed to start tunnel: %s", exc) + print(f"启动隧道失败:{exc}") + return + finally: + signal.signal(signal.SIGINT, original_sigint_handler) logger.info("tunnel started") try: logger.debug(f"tunnel address: {address}, port: {port}") @@ -90,4 +96,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main()