pymobiledevice3 4.14.6__py3-none-any.whl → 7.0.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- misc/plist_sniffer.py +15 -15
- misc/remotexpc_sniffer.py +29 -28
- misc/understanding_idevice_protocol_layers.md +15 -10
- pymobiledevice3/__main__.py +317 -127
- pymobiledevice3/_version.py +22 -4
- pymobiledevice3/bonjour.py +358 -113
- pymobiledevice3/ca.py +253 -16
- pymobiledevice3/cli/activation.py +31 -23
- pymobiledevice3/cli/afc.py +49 -40
- pymobiledevice3/cli/amfi.py +16 -21
- pymobiledevice3/cli/apps.py +87 -42
- pymobiledevice3/cli/backup.py +160 -90
- pymobiledevice3/cli/bonjour.py +44 -40
- pymobiledevice3/cli/cli_common.py +204 -198
- pymobiledevice3/cli/companion_proxy.py +14 -14
- pymobiledevice3/cli/crash.py +105 -56
- pymobiledevice3/cli/developer/__init__.py +62 -0
- pymobiledevice3/cli/developer/accessibility/__init__.py +65 -0
- pymobiledevice3/cli/developer/accessibility/settings.py +43 -0
- pymobiledevice3/cli/developer/arbitration.py +50 -0
- pymobiledevice3/cli/developer/condition.py +33 -0
- pymobiledevice3/cli/developer/core_device.py +294 -0
- pymobiledevice3/cli/developer/debugserver.py +244 -0
- pymobiledevice3/cli/developer/dvt/__init__.py +438 -0
- pymobiledevice3/cli/developer/dvt/core_profile_session.py +295 -0
- pymobiledevice3/cli/developer/dvt/simulate_location.py +56 -0
- pymobiledevice3/cli/developer/dvt/sysmon/__init__.py +69 -0
- pymobiledevice3/cli/developer/dvt/sysmon/process.py +188 -0
- pymobiledevice3/cli/developer/fetch_symbols.py +108 -0
- pymobiledevice3/cli/developer/simulate_location.py +51 -0
- pymobiledevice3/cli/diagnostics/__init__.py +75 -0
- pymobiledevice3/cli/diagnostics/battery.py +47 -0
- pymobiledevice3/cli/idam.py +42 -0
- pymobiledevice3/cli/lockdown.py +108 -103
- pymobiledevice3/cli/mounter.py +158 -99
- pymobiledevice3/cli/notification.py +38 -26
- pymobiledevice3/cli/pcap.py +45 -24
- pymobiledevice3/cli/power_assertion.py +18 -17
- pymobiledevice3/cli/processes.py +17 -23
- pymobiledevice3/cli/profile.py +165 -109
- pymobiledevice3/cli/provision.py +35 -34
- pymobiledevice3/cli/remote.py +217 -129
- pymobiledevice3/cli/restore.py +159 -143
- pymobiledevice3/cli/springboard.py +63 -53
- pymobiledevice3/cli/syslog.py +193 -86
- pymobiledevice3/cli/usbmux.py +73 -33
- pymobiledevice3/cli/version.py +5 -7
- pymobiledevice3/cli/webinspector.py +376 -214
- pymobiledevice3/common.py +3 -1
- pymobiledevice3/exceptions.py +182 -58
- pymobiledevice3/irecv.py +52 -53
- pymobiledevice3/irecv_devices.py +1489 -464
- pymobiledevice3/lockdown.py +473 -275
- pymobiledevice3/lockdown_service_provider.py +15 -8
- pymobiledevice3/osu/os_utils.py +27 -9
- pymobiledevice3/osu/posix_util.py +34 -15
- pymobiledevice3/osu/win_util.py +14 -8
- pymobiledevice3/pair_records.py +102 -21
- pymobiledevice3/remote/common.py +8 -4
- pymobiledevice3/remote/core_device/app_service.py +94 -67
- pymobiledevice3/remote/core_device/core_device_service.py +17 -14
- pymobiledevice3/remote/core_device/device_info.py +5 -5
- pymobiledevice3/remote/core_device/diagnostics_service.py +19 -4
- pymobiledevice3/remote/core_device/file_service.py +53 -23
- pymobiledevice3/remote/remote_service_discovery.py +79 -45
- pymobiledevice3/remote/remotexpc.py +73 -44
- pymobiledevice3/remote/tunnel_service.py +442 -317
- pymobiledevice3/remote/utils.py +14 -13
- pymobiledevice3/remote/xpc_message.py +145 -125
- pymobiledevice3/resources/dsc_uuid_map.py +19 -19
- pymobiledevice3/resources/firmware_notifications.py +20 -16
- pymobiledevice3/resources/notifications.txt +144 -0
- pymobiledevice3/restore/asr.py +27 -27
- pymobiledevice3/restore/base_restore.py +110 -21
- pymobiledevice3/restore/consts.py +87 -66
- pymobiledevice3/restore/device.py +59 -12
- pymobiledevice3/restore/fdr.py +46 -48
- pymobiledevice3/restore/ftab.py +19 -19
- pymobiledevice3/restore/img4.py +163 -0
- pymobiledevice3/restore/mbn.py +587 -0
- pymobiledevice3/restore/recovery.py +151 -151
- pymobiledevice3/restore/restore.py +562 -544
- pymobiledevice3/restore/restore_options.py +131 -110
- pymobiledevice3/restore/restored_client.py +51 -31
- pymobiledevice3/restore/tss.py +385 -267
- pymobiledevice3/service_connection.py +252 -59
- pymobiledevice3/services/accessibilityaudit.py +202 -120
- pymobiledevice3/services/afc.py +962 -365
- pymobiledevice3/services/amfi.py +24 -30
- pymobiledevice3/services/companion.py +23 -19
- pymobiledevice3/services/crash_reports.py +71 -47
- pymobiledevice3/services/debugserver_applist.py +3 -3
- pymobiledevice3/services/device_arbitration.py +8 -8
- pymobiledevice3/services/device_link.py +101 -79
- pymobiledevice3/services/diagnostics.py +973 -967
- pymobiledevice3/services/dtfetchsymbols.py +8 -8
- pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
- pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
- pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
- pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
- pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
- pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
- pymobiledevice3/services/dvt/instruments/device_info.py +20 -11
- pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
- pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
- pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
- pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
- pymobiledevice3/services/dvt/instruments/process_control.py +35 -10
- pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
- pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
- pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
- pymobiledevice3/services/file_relay.py +10 -10
- pymobiledevice3/services/heartbeat.py +9 -8
- pymobiledevice3/services/house_arrest.py +16 -15
- pymobiledevice3/services/idam.py +20 -0
- pymobiledevice3/services/installation_proxy.py +173 -81
- pymobiledevice3/services/lockdown_service.py +20 -10
- pymobiledevice3/services/misagent.py +22 -19
- pymobiledevice3/services/mobile_activation.py +147 -64
- pymobiledevice3/services/mobile_config.py +331 -294
- pymobiledevice3/services/mobile_image_mounter.py +141 -113
- pymobiledevice3/services/mobilebackup2.py +203 -145
- pymobiledevice3/services/notification_proxy.py +11 -11
- pymobiledevice3/services/os_trace.py +134 -74
- pymobiledevice3/services/pcapd.py +314 -302
- pymobiledevice3/services/power_assertion.py +10 -9
- pymobiledevice3/services/preboard.py +4 -4
- pymobiledevice3/services/remote_fetch_symbols.py +21 -14
- pymobiledevice3/services/remote_server.py +176 -146
- pymobiledevice3/services/restore_service.py +16 -16
- pymobiledevice3/services/screenshot.py +15 -12
- pymobiledevice3/services/simulate_location.py +7 -7
- pymobiledevice3/services/springboard.py +15 -15
- pymobiledevice3/services/syslog.py +5 -5
- pymobiledevice3/services/web_protocol/alert.py +11 -11
- pymobiledevice3/services/web_protocol/automation_session.py +251 -239
- pymobiledevice3/services/web_protocol/cdp_screencast.py +46 -37
- pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
- pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
- pymobiledevice3/services/web_protocol/driver.py +114 -111
- pymobiledevice3/services/web_protocol/element.py +124 -111
- pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
- pymobiledevice3/services/web_protocol/selenium_api.py +49 -49
- pymobiledevice3/services/web_protocol/session_protocol.py +18 -12
- pymobiledevice3/services/web_protocol/switch_to.py +30 -27
- pymobiledevice3/services/webinspector.py +189 -155
- pymobiledevice3/tcp_forwarder.py +87 -69
- pymobiledevice3/tunneld/__init__.py +0 -0
- pymobiledevice3/tunneld/api.py +63 -0
- pymobiledevice3/tunneld/server.py +603 -0
- pymobiledevice3/usbmux.py +198 -147
- pymobiledevice3/utils.py +14 -11
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/METADATA +55 -28
- pymobiledevice3-7.0.6.dist-info/RECORD +188 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/WHEEL +1 -1
- pymobiledevice3/cli/developer.py +0 -1215
- pymobiledevice3/cli/diagnostics.py +0 -99
- pymobiledevice3/tunneld.py +0 -524
- pymobiledevice3-4.14.6.dist-info/RECORD +0 -168
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info/licenses}/LICENSE +0 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/top_level.txt +0 -0
pymobiledevice3/cli/remote.py
CHANGED
|
@@ -3,24 +3,36 @@ import dataclasses
|
|
|
3
3
|
import logging
|
|
4
4
|
import sys
|
|
5
5
|
import tempfile
|
|
6
|
+
from contextlib import nullcontext
|
|
6
7
|
from functools import partial
|
|
7
|
-
from
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Annotated, Optional, TextIO
|
|
8
10
|
|
|
9
|
-
import
|
|
11
|
+
import typer
|
|
12
|
+
from typer_injector import InjectingTyper
|
|
10
13
|
|
|
11
14
|
from pymobiledevice3.bonjour import DEFAULT_BONJOUR_TIMEOUT, browse_remotepairing_manual_pairing
|
|
12
|
-
from pymobiledevice3.cli.cli_common import
|
|
13
|
-
|
|
15
|
+
from pymobiledevice3.cli.cli_common import (
|
|
16
|
+
RSDServiceProviderDep,
|
|
17
|
+
print_json,
|
|
18
|
+
prompt_device_list,
|
|
19
|
+
sudo_required,
|
|
20
|
+
user_requested_colored_output,
|
|
21
|
+
)
|
|
14
22
|
from pymobiledevice3.common import get_home_folder
|
|
15
23
|
from pymobiledevice3.exceptions import NoDeviceConnectedError
|
|
16
24
|
from pymobiledevice3.pair_records import PAIRING_RECORD_EXT, get_remote_pairing_record_filename
|
|
17
25
|
from pymobiledevice3.remote.common import ConnectionType, TunnelProtocol
|
|
18
26
|
from pymobiledevice3.remote.module_imports import MAX_IDLE_TIMEOUT, start_tunnel, verify_tunnel_imports
|
|
19
27
|
from pymobiledevice3.remote.remote_service_discovery import RSD_PORT, RemoteServiceDiscoveryService
|
|
20
|
-
from pymobiledevice3.remote.tunnel_service import
|
|
21
|
-
|
|
28
|
+
from pymobiledevice3.remote.tunnel_service import (
|
|
29
|
+
RemotePairingManualPairingService,
|
|
30
|
+
get_core_device_tunnel_services,
|
|
31
|
+
get_remote_pairing_tunnel_services,
|
|
32
|
+
)
|
|
22
33
|
from pymobiledevice3.remote.utils import get_rsds
|
|
23
|
-
from pymobiledevice3.tunneld import TUNNELD_DEFAULT_ADDRESS
|
|
34
|
+
from pymobiledevice3.tunneld.api import TUNNELD_DEFAULT_ADDRESS
|
|
35
|
+
from pymobiledevice3.tunneld.server import TunneldRunner
|
|
24
36
|
|
|
25
37
|
logger = logging.getLogger(__name__)
|
|
26
38
|
|
|
@@ -28,133 +40,170 @@ logger = logging.getLogger(__name__)
|
|
|
28
40
|
async def browse_rsd(timeout: float = DEFAULT_BONJOUR_TIMEOUT) -> list[dict]:
|
|
29
41
|
devices = []
|
|
30
42
|
for rsd in await get_rsds(timeout):
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
assert rsd.peer_info is not None
|
|
44
|
+
devices.append({
|
|
45
|
+
"address": rsd.service.address[0],
|
|
46
|
+
"port": RSD_PORT,
|
|
47
|
+
"UniqueDeviceID": rsd.peer_info["Properties"]["UniqueDeviceID"],
|
|
48
|
+
"ProductType": rsd.peer_info["Properties"]["ProductType"],
|
|
49
|
+
"OSVersion": rsd.peer_info["Properties"]["OSVersion"],
|
|
50
|
+
})
|
|
36
51
|
return devices
|
|
37
52
|
|
|
38
53
|
|
|
39
54
|
async def browse_remotepairing(timeout: float = DEFAULT_BONJOUR_TIMEOUT) -> list[dict]:
|
|
40
55
|
devices = []
|
|
41
56
|
for remotepairing in await get_remote_pairing_tunnel_services(timeout):
|
|
42
|
-
devices.append({
|
|
43
|
-
|
|
44
|
-
|
|
57
|
+
devices.append({
|
|
58
|
+
"address": remotepairing.hostname,
|
|
59
|
+
"port": remotepairing.port,
|
|
60
|
+
"identifier": remotepairing.remote_identifier,
|
|
61
|
+
})
|
|
45
62
|
return devices
|
|
46
63
|
|
|
47
64
|
|
|
48
65
|
async def cli_browse(timeout: float = DEFAULT_BONJOUR_TIMEOUT) -> None:
|
|
49
66
|
print_json({
|
|
50
|
-
|
|
51
|
-
|
|
67
|
+
"usb": await browse_rsd(timeout),
|
|
68
|
+
"wifi": await browse_remotepairing(timeout),
|
|
52
69
|
})
|
|
53
70
|
|
|
54
71
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
72
|
+
cli = InjectingTyper(
|
|
73
|
+
name="remote",
|
|
74
|
+
help="Create and browse RemoteXPC tunnels (RSD/tunneld) for developer services.",
|
|
75
|
+
no_args_is_help=True,
|
|
76
|
+
)
|
|
58
77
|
|
|
59
78
|
|
|
60
|
-
@cli.
|
|
61
|
-
def remote_cli() -> None:
|
|
62
|
-
""" Create RemoteXPC tunnels """
|
|
63
|
-
pass
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
@remote_cli.command('tunneld', cls=BaseCommand)
|
|
67
|
-
@click.option('--host', default=TUNNELD_DEFAULT_ADDRESS[0])
|
|
68
|
-
@click.option('--port', type=click.INT, default=TUNNELD_DEFAULT_ADDRESS[1])
|
|
69
|
-
@click.option('-d', '--daemonize', is_flag=True)
|
|
70
|
-
@click.option('-p', '--protocol', type=click.Choice([e.value for e in TunnelProtocol]),
|
|
71
|
-
default=TunnelProtocol.QUIC.value)
|
|
72
|
-
@click.option('--usb/--no-usb', default=True, help='Enable usb monitoring')
|
|
73
|
-
@click.option('--wifi/--no-wifi', default=True, help='Enable wifi monitoring')
|
|
74
|
-
@click.option('--usbmux/--no-usbmux', default=True, help='Enable usbmux monitoring')
|
|
75
|
-
@click.option('--mobdev2/--no-mobdev2', default=True, help='Enable mobdev2 monitoring')
|
|
79
|
+
@cli.command("tunneld")
|
|
76
80
|
@sudo_required
|
|
77
81
|
def cli_tunneld(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
"""
|
|
82
|
+
host: Annotated[str, typer.Option(help="Address to bind the tunneld server to.")] = TUNNELD_DEFAULT_ADDRESS[0],
|
|
83
|
+
port: Annotated[int, typer.Option(help="Port to bind the tunneld server to.")] = TUNNELD_DEFAULT_ADDRESS[1],
|
|
84
|
+
daemonize: Annotated[bool, typer.Option("--daemonize", "-d", help="Run tunneld in the background.")] = False,
|
|
85
|
+
protocol: Annotated[
|
|
86
|
+
TunnelProtocol,
|
|
87
|
+
typer.Option(
|
|
88
|
+
"--protocol",
|
|
89
|
+
"-p",
|
|
90
|
+
case_sensitive=False,
|
|
91
|
+
help="Transport protocol for tunneld (default: TCP on Python >=3.13, otherwise QUIC).",
|
|
92
|
+
),
|
|
93
|
+
] = TunnelProtocol.DEFAULT,
|
|
94
|
+
usb: Annotated[bool, typer.Option(help="Enable USB monitoring")] = True,
|
|
95
|
+
wifi: Annotated[bool, typer.Option(help="Enable WiFi monitoring")] = True,
|
|
96
|
+
usbmux: Annotated[bool, typer.Option(help="Enable usbmux monitoring")] = True,
|
|
97
|
+
mobdev2: Annotated[bool, typer.Option(help="Enable mobdev2 monitoring")] = True,
|
|
98
|
+
) -> None:
|
|
99
|
+
"""Start Tunneld service for remote tunneling"""
|
|
81
100
|
if not verify_tunnel_imports():
|
|
82
101
|
return
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
102
|
+
tunneld_runner = partial(
|
|
103
|
+
TunneldRunner.create,
|
|
104
|
+
host,
|
|
105
|
+
port,
|
|
106
|
+
protocol=protocol,
|
|
107
|
+
usb_monitor=usb,
|
|
108
|
+
wifi_monitor=wifi,
|
|
109
|
+
usbmux_monitor=usbmux,
|
|
110
|
+
mobdev2_monitor=mobdev2,
|
|
111
|
+
)
|
|
86
112
|
if daemonize:
|
|
87
113
|
try:
|
|
88
114
|
from daemonize import Daemonize
|
|
89
|
-
except ImportError:
|
|
90
|
-
raise NotImplementedError(
|
|
91
|
-
with tempfile.NamedTemporaryFile(
|
|
92
|
-
daemon = Daemonize(app=f
|
|
93
|
-
|
|
94
|
-
logger.info(f'starting Tunneld {host}:{port}')
|
|
115
|
+
except ImportError as e:
|
|
116
|
+
raise NotImplementedError("daemonizing is only supported on unix platforms") from e
|
|
117
|
+
with tempfile.NamedTemporaryFile("wt") as pid_file:
|
|
118
|
+
daemon = Daemonize(app=f"Tunneld {host}:{port}", pid=pid_file.name, action=tunneld_runner)
|
|
119
|
+
logger.info(f"starting Tunneld {host}:{port}")
|
|
95
120
|
daemon.start()
|
|
96
121
|
else:
|
|
97
122
|
tunneld_runner()
|
|
98
123
|
|
|
99
124
|
|
|
100
|
-
@
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
125
|
+
@cli.command("browse")
|
|
126
|
+
def browse(
|
|
127
|
+
timeout: Annotated[float, typer.Option(help="Bonjour timeout (in seconds)")] = DEFAULT_BONJOUR_TIMEOUT,
|
|
128
|
+
) -> None:
|
|
129
|
+
"""browse RemoteXPC devices using bonjour"""
|
|
104
130
|
asyncio.run(cli_browse(timeout), debug=True)
|
|
105
131
|
|
|
106
132
|
|
|
107
|
-
@
|
|
108
|
-
def rsd_info(service_provider:
|
|
109
|
-
"""
|
|
133
|
+
@cli.command("rsd-info")
|
|
134
|
+
def rsd_info(service_provider: RSDServiceProviderDep) -> None:
|
|
135
|
+
"""show info extracted from RSD peer"""
|
|
110
136
|
print_json(service_provider.peer_info)
|
|
111
137
|
|
|
112
138
|
|
|
113
139
|
async def tunnel_task(
|
|
114
|
-
|
|
115
|
-
|
|
140
|
+
service,
|
|
141
|
+
secrets: Optional[TextIO] = None,
|
|
142
|
+
script_mode: bool = False,
|
|
143
|
+
max_idle_timeout: float = MAX_IDLE_TIMEOUT,
|
|
144
|
+
protocol: TunnelProtocol = TunnelProtocol.DEFAULT,
|
|
145
|
+
) -> None:
|
|
116
146
|
async with start_tunnel(
|
|
117
|
-
|
|
118
|
-
|
|
147
|
+
service, secrets=secrets, max_idle_timeout=max_idle_timeout, protocol=protocol
|
|
148
|
+
) as tunnel_result:
|
|
149
|
+
logger.info("tunnel created")
|
|
119
150
|
if script_mode:
|
|
120
|
-
print(f
|
|
151
|
+
print(f"{tunnel_result.address} {tunnel_result.port}")
|
|
121
152
|
else:
|
|
122
153
|
if user_requested_colored_output():
|
|
123
154
|
if secrets is not None:
|
|
124
|
-
print(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
print(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
print(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
print(
|
|
137
|
-
|
|
155
|
+
print(
|
|
156
|
+
typer.style("Secrets: ", bold=True, fg="magenta")
|
|
157
|
+
+ typer.style(secrets.name, bold=True, fg="white")
|
|
158
|
+
)
|
|
159
|
+
print(
|
|
160
|
+
typer.style("Identifier: ", bold=True, fg="yellow")
|
|
161
|
+
+ typer.style(service.remote_identifier, bold=True, fg="white")
|
|
162
|
+
)
|
|
163
|
+
print(
|
|
164
|
+
typer.style("Interface: ", bold=True, fg="yellow")
|
|
165
|
+
+ typer.style(tunnel_result.interface, bold=True, fg="white")
|
|
166
|
+
)
|
|
167
|
+
print(
|
|
168
|
+
typer.style("Protocol: ", bold=True, fg="yellow")
|
|
169
|
+
+ typer.style(tunnel_result.protocol, bold=True, fg="white")
|
|
170
|
+
)
|
|
171
|
+
print(
|
|
172
|
+
typer.style("RSD Address: ", bold=True, fg="yellow")
|
|
173
|
+
+ typer.style(tunnel_result.address, bold=True, fg="white")
|
|
174
|
+
)
|
|
175
|
+
print(
|
|
176
|
+
typer.style("RSD Port: ", bold=True, fg="yellow")
|
|
177
|
+
+ typer.style(tunnel_result.port, bold=True, fg="white")
|
|
178
|
+
)
|
|
179
|
+
print(
|
|
180
|
+
typer.style("Use the follow connection option:\n", bold=True, fg="yellow")
|
|
181
|
+
+ typer.style(f"--rsd {tunnel_result.address} {tunnel_result.port}", bold=True, fg="cyan")
|
|
182
|
+
)
|
|
138
183
|
else:
|
|
139
184
|
if secrets is not None:
|
|
140
|
-
print(f
|
|
141
|
-
print(f
|
|
142
|
-
print(f
|
|
143
|
-
print(f
|
|
144
|
-
print(f
|
|
145
|
-
print(f
|
|
146
|
-
print(f
|
|
147
|
-
f'--rsd {tunnel_result.address} {tunnel_result.port}')
|
|
185
|
+
print(f"Secrets: {secrets.name}")
|
|
186
|
+
print(f"Identifier: {service.remote_identifier}")
|
|
187
|
+
print(f"Interface: {tunnel_result.interface}")
|
|
188
|
+
print(f"Protocol: {tunnel_result.protocol}")
|
|
189
|
+
print(f"RSD Address: {tunnel_result.address}")
|
|
190
|
+
print(f"RSD Port: {tunnel_result.port}")
|
|
191
|
+
print(f"Use the follow connection option:\n--rsd {tunnel_result.address} {tunnel_result.port}")
|
|
148
192
|
sys.stdout.flush()
|
|
149
193
|
await tunnel_result.client.wait_closed()
|
|
150
|
-
logger.info(
|
|
194
|
+
logger.info("tunnel was closed")
|
|
151
195
|
|
|
152
196
|
|
|
153
197
|
async def start_tunnel_task(
|
|
154
|
-
|
|
155
|
-
|
|
198
|
+
connection_type: ConnectionType,
|
|
199
|
+
secrets: Optional[TextIO],
|
|
200
|
+
udid: Optional[str] = None,
|
|
201
|
+
script_mode: bool = False,
|
|
202
|
+
max_idle_timeout: float = MAX_IDLE_TIMEOUT,
|
|
203
|
+
protocol: TunnelProtocol = TunnelProtocol.DEFAULT,
|
|
204
|
+
) -> None:
|
|
156
205
|
if start_tunnel is None:
|
|
157
|
-
raise NotImplementedError(
|
|
206
|
+
raise NotImplementedError("failed to start the tunnel on your platform")
|
|
158
207
|
get_tunnel_services = {
|
|
159
208
|
connection_type.USB: get_core_device_tunnel_services,
|
|
160
209
|
connection_type.WIFI: get_remote_pairing_tunnel_services,
|
|
@@ -170,33 +219,64 @@ async def start_tunnel_task(
|
|
|
170
219
|
# several devices were found, show prompt if none explicitly selected
|
|
171
220
|
service = prompt_device_list(tunnel_services)
|
|
172
221
|
|
|
173
|
-
await tunnel_task(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
@
|
|
179
|
-
default=ConnectionType.USB.value)
|
|
180
|
-
@click.option('--udid', help='UDID for a specific device to look for')
|
|
181
|
-
@click.option('--secrets', type=click.File('wt'), help='TLS keyfile for decrypting with Wireshark')
|
|
182
|
-
@click.option('--script-mode', is_flag=True,
|
|
183
|
-
help='Show only HOST and port number to allow easy parsing from external shell scripts')
|
|
184
|
-
@click.option('--max-idle-timeout', type=click.FLOAT, default=MAX_IDLE_TIMEOUT,
|
|
185
|
-
help='Maximum QUIC idle time (ping interval)')
|
|
186
|
-
@click.option('-p', '--protocol',
|
|
187
|
-
type=click.Choice([e.value for e in TunnelProtocol], case_sensitive=False),
|
|
188
|
-
default=TunnelProtocol.QUIC.value)
|
|
222
|
+
await tunnel_task(
|
|
223
|
+
service, secrets=secrets, script_mode=script_mode, max_idle_timeout=max_idle_timeout, protocol=protocol
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@cli.command("start-tunnel")
|
|
189
228
|
@sudo_required
|
|
190
229
|
def cli_start_tunnel(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
230
|
+
connection_type: Annotated[
|
|
231
|
+
ConnectionType,
|
|
232
|
+
typer.Option(
|
|
233
|
+
"--connection-type",
|
|
234
|
+
"-t",
|
|
235
|
+
case_sensitive=False,
|
|
236
|
+
help="Connection interface to tunnel (USB, WiFi, etc.).",
|
|
237
|
+
),
|
|
238
|
+
] = ConnectionType.USB,
|
|
239
|
+
udid: Annotated[
|
|
240
|
+
Optional[str],
|
|
241
|
+
typer.Option(help="UDID for a specific device to look for"),
|
|
242
|
+
] = None,
|
|
243
|
+
secrets: Annotated[
|
|
244
|
+
Optional[Path],
|
|
245
|
+
typer.Option(help="File to write TLS secrets for Wireshark decryption."),
|
|
246
|
+
] = None,
|
|
247
|
+
script_mode: Annotated[
|
|
248
|
+
bool,
|
|
249
|
+
typer.Option(help="Print only HOST and port for scripts instead of formatted output."),
|
|
250
|
+
] = False,
|
|
251
|
+
max_idle_timeout: Annotated[
|
|
252
|
+
float,
|
|
253
|
+
typer.Option(help="Maximum idle time before QUIC keepalive pings are sent."),
|
|
254
|
+
] = MAX_IDLE_TIMEOUT,
|
|
255
|
+
protocol: Annotated[
|
|
256
|
+
TunnelProtocol,
|
|
257
|
+
typer.Option(
|
|
258
|
+
"--protocol",
|
|
259
|
+
"-p",
|
|
260
|
+
case_sensitive=False,
|
|
261
|
+
help="Transport protocol for the tunnel (default: TCP on Python >=3.13, otherwise QUIC).",
|
|
262
|
+
),
|
|
263
|
+
] = TunnelProtocol.DEFAULT,
|
|
264
|
+
) -> None:
|
|
265
|
+
"""start tunnel"""
|
|
194
266
|
if not verify_tunnel_imports():
|
|
195
267
|
return
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
268
|
+
with secrets.open("wt") if secrets is not None else nullcontext() as secrets_file:
|
|
269
|
+
asyncio.run(
|
|
270
|
+
start_tunnel_task(
|
|
271
|
+
connection_type,
|
|
272
|
+
secrets_file,
|
|
273
|
+
udid,
|
|
274
|
+
script_mode,
|
|
275
|
+
max_idle_timeout=max_idle_timeout,
|
|
276
|
+
protocol=protocol,
|
|
277
|
+
),
|
|
278
|
+
debug=True,
|
|
279
|
+
)
|
|
200
280
|
|
|
201
281
|
|
|
202
282
|
@dataclasses.dataclass
|
|
@@ -207,44 +287,53 @@ class RemotePairingManualPairingDevice:
|
|
|
207
287
|
identifier: str
|
|
208
288
|
|
|
209
289
|
|
|
210
|
-
async def start_remote_pair_task(device_name: str) -> None:
|
|
290
|
+
async def start_remote_pair_task(device_name: Optional[str]) -> None:
|
|
211
291
|
if start_tunnel is None:
|
|
212
|
-
raise NotImplementedError(
|
|
292
|
+
raise NotImplementedError("failed to start the tunnel on your platform")
|
|
213
293
|
|
|
214
294
|
devices: list[RemotePairingManualPairingDevice] = []
|
|
215
295
|
for answer in await browse_remotepairing_manual_pairing():
|
|
216
|
-
current_device_name = answer.properties[b
|
|
296
|
+
current_device_name = answer.properties[b"name"].decode()
|
|
217
297
|
|
|
218
298
|
if device_name is not None and current_device_name != device_name:
|
|
219
299
|
continue
|
|
220
300
|
|
|
221
|
-
for
|
|
222
|
-
devices.append(
|
|
223
|
-
|
|
301
|
+
for address in answer.addresses:
|
|
302
|
+
devices.append(
|
|
303
|
+
RemotePairingManualPairingDevice(
|
|
304
|
+
ip=address.full_ip,
|
|
305
|
+
port=answer.port,
|
|
306
|
+
device_name=current_device_name,
|
|
307
|
+
identifier=answer.properties[b"identifier"].decode(),
|
|
308
|
+
)
|
|
309
|
+
)
|
|
224
310
|
|
|
225
311
|
if len(devices) > 0:
|
|
226
312
|
device = prompt_device_list(devices)
|
|
227
313
|
else:
|
|
228
|
-
logger.error(
|
|
314
|
+
logger.error("No devices were found during bonjour browse")
|
|
229
315
|
return
|
|
230
316
|
|
|
231
317
|
async with RemotePairingManualPairingService(device.identifier, device.ip, device.port) as service:
|
|
232
318
|
await service.connect(autopair=True)
|
|
233
319
|
|
|
234
320
|
|
|
235
|
-
@
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
321
|
+
@cli.command("pair")
|
|
322
|
+
def cli_pair(
|
|
323
|
+
name: Annotated[
|
|
324
|
+
Optional[str],
|
|
325
|
+
typer.Option(help="Device name for a specific device to look for"),
|
|
326
|
+
] = None,
|
|
327
|
+
) -> None:
|
|
328
|
+
"""start remote pairing for devices which allow"""
|
|
239
329
|
asyncio.run(start_remote_pair_task(name), debug=True)
|
|
240
330
|
|
|
241
331
|
|
|
242
|
-
@
|
|
243
|
-
@click.argument('udid')
|
|
332
|
+
@cli.command("delete-pair")
|
|
244
333
|
@sudo_required
|
|
245
|
-
def cli_delete_pair(udid: str):
|
|
246
|
-
"""
|
|
247
|
-
pair_record_path = get_home_folder() / f
|
|
334
|
+
def cli_delete_pair(udid: str) -> None:
|
|
335
|
+
"""delete a pairing record"""
|
|
336
|
+
pair_record_path = get_home_folder() / f"{get_remote_pairing_record_filename(udid)}.{PAIRING_RECORD_EXT}"
|
|
248
337
|
pair_record_path.unlink()
|
|
249
338
|
|
|
250
339
|
|
|
@@ -253,8 +342,7 @@ async def cli_service_task(service_provider: RemoteServiceDiscoveryService, serv
|
|
|
253
342
|
service.shell()
|
|
254
343
|
|
|
255
344
|
|
|
256
|
-
@
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
""" start an ipython shell for interacting with given service """
|
|
345
|
+
@cli.command("service")
|
|
346
|
+
def cli_service(service_provider: RSDServiceProviderDep, service_name: str) -> None:
|
|
347
|
+
"""start an ipython shell for interacting with given service"""
|
|
260
348
|
asyncio.run(cli_service_task(service_provider, service_name), debug=True)
|