import asyncio import dataclasses import json import logging import random import sys import socketio import tempfile from contextlib import nullcontext from functools import partial from pathlib import Path from typing import Annotated, Optional, TextIO import typer from typer_injector import InjectingTyper from pymobiledevice3.bonjour import DEFAULT_BONJOUR_TIMEOUT, browse_remotepairing_manual_pairing from pymobiledevice3.cli.cli_common import ( RSDServiceProviderDep, async_command, print_json, prompt_device_list, sudo_required, user_requested_colored_output, ) from pymobiledevice3.common import get_home_folder from pymobiledevice3.exceptions import NoDeviceConnectedError from pymobiledevice3.pair_records import PAIRING_RECORD_EXT, get_remote_pairing_record_filename from pymobiledevice3.remote.common import ConnectionType, TunnelProtocol from pymobiledevice3.remote.module_imports import MAX_IDLE_TIMEOUT, start_tunnel, verify_tunnel_imports from pymobiledevice3.remote.remote_service_discovery import RSD_PORT from pymobiledevice3.remote.tunnel_service import ( RemotePairingManualPairingService, get_core_device_tunnel_services, get_remote_pairing_tunnel_services, ) from pymobiledevice3.remote.utils import get_rsds from pymobiledevice3.tunneld.api import TUNNELD_DEFAULT_ADDRESS from pymobiledevice3.utils import run_in_loop from server import TunneldRunnerSio, LocationSimulationState, logger def main(): cli_tunneld(host="0.0.0.0", port=8000) def cli_tunneld( host: Annotated[str, typer.Option(help="Address to bind the tunneld server to.")] = TUNNELD_DEFAULT_ADDRESS[0], port: Annotated[int, typer.Option(help="Port to bind the tunneld server to.")] = TUNNELD_DEFAULT_ADDRESS[1], daemonize: Annotated[bool, typer.Option("--daemonize", "-d", help="Run tunneld in the background.")] = False, protocol: Annotated[ TunnelProtocol, typer.Option( "--protocol", "-p", case_sensitive=False, help="Transport protocol for tunneld (default: TCP on Python >=3.13, otherwise QUIC).", ), ] = TunnelProtocol.DEFAULT, usb: Annotated[bool, typer.Option(help="Enable USB monitoring")] = True, wifi: Annotated[bool, typer.Option(help="Enable WiFi monitoring")] = True, usbmux: Annotated[bool, typer.Option(help="Enable usbmux monitoring")] = True, mobdev2: Annotated[bool, typer.Option(help="Enable mobdev2 monitoring")] = True, context: Annotated[LocationSimulationState, typer.Option( help="Location simulation context to use for the server.")] = LocationSimulationState(), ) -> None: """Start Tunneld service for remote tunneling""" if not verify_tunnel_imports(): return tunneld_runner = partial( TunneldRunnerSio.create, host, port, protocol=protocol, usb_monitor=usb, wifi_monitor=wifi, usbmux_monitor=usbmux, mobdev2_monitor=mobdev2, context=context, ) if daemonize: try: from daemonize import Daemonize except ImportError as e: raise NotImplementedError("daemonizing is only supported on unix platforms") from e with tempfile.NamedTemporaryFile("wt") as pid_file: daemon = Daemonize(app=f"Tunneld {host}:{port}", pid=pid_file.name, action=tunneld_runner) logger.info(f"starting Tunneld {host}:{port}") daemon.start() else: tunneld_runner() # 4. Entry point (always last) if __name__ == "__main__": main()