pymobiledevice3 4.27.7__py3-none-any.whl → 5.0.1__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.
Potentially problematic release.
This version of pymobiledevice3 might be problematic. Click here for more details.
- pymobiledevice3/__main__.py +0 -1
- pymobiledevice3/_version.py +2 -2
- pymobiledevice3/bonjour.py +375 -118
- pymobiledevice3/cli/bonjour.py +4 -4
- pymobiledevice3/cli/lockdown.py +7 -2
- pymobiledevice3/cli/remote.py +4 -3
- pymobiledevice3/lockdown.py +7 -12
- pymobiledevice3/remote/tunnel_service.py +16 -8
- pymobiledevice3/remote/utils.py +2 -2
- pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +62 -1
- pymobiledevice3/tunneld/server.py +118 -79
- pymobiledevice3/usbmux.py +16 -12
- pymobiledevice3/utils.py +1 -1
- {pymobiledevice3-4.27.7.dist-info → pymobiledevice3-5.0.1.dist-info}/METADATA +1 -2
- {pymobiledevice3-4.27.7.dist-info → pymobiledevice3-5.0.1.dist-info}/RECORD +19 -19
- {pymobiledevice3-4.27.7.dist-info → pymobiledevice3-5.0.1.dist-info}/WHEEL +0 -0
- {pymobiledevice3-4.27.7.dist-info → pymobiledevice3-5.0.1.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-4.27.7.dist-info → pymobiledevice3-5.0.1.dist-info}/licenses/LICENSE +0 -0
- {pymobiledevice3-4.27.7.dist-info → pymobiledevice3-5.0.1.dist-info}/top_level.txt +0 -0
|
@@ -190,7 +190,7 @@ class RemotePairingTunnel(ABC):
|
|
|
190
190
|
self._tun_read_task = asyncio.create_task(self.tun_read_task(), name=f'tun-read-{address}')
|
|
191
191
|
|
|
192
192
|
async def stop_tunnel(self) -> None:
|
|
193
|
-
self._logger.debug('stopping tunnel')
|
|
193
|
+
self._logger.debug(f'[{asyncio.current_task().get_name()}] stopping tunnel')
|
|
194
194
|
self._tun_read_task.cancel()
|
|
195
195
|
with suppress(CancelledError):
|
|
196
196
|
await self._tun_read_task
|
|
@@ -223,6 +223,9 @@ class RemotePairingQuicTunnel(RemotePairingTunnel, QuicConnectionProtocol):
|
|
|
223
223
|
self._quic.send_datagram_frame(packet)
|
|
224
224
|
self.transmit()
|
|
225
225
|
|
|
226
|
+
# Allow other tasks to run
|
|
227
|
+
await asyncio.sleep(0)
|
|
228
|
+
|
|
226
229
|
async def request_tunnel_establish(self) -> dict:
|
|
227
230
|
stream_id = self._quic.get_next_available_stream_id()
|
|
228
231
|
# pad the data with random data to force the MTU size correctly
|
|
@@ -939,17 +942,21 @@ class RemotePairingManualPairingService(RemotePairingTunnelService):
|
|
|
939
942
|
class CoreDeviceTunnelProxy(StartTcpTunnel):
|
|
940
943
|
SERVICE_NAME = 'com.apple.internal.devicecompute.CoreDeviceProxy'
|
|
941
944
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
+
@classmethod
|
|
946
|
+
async def create(cls, lockdown: LockdownServiceProvider) -> 'CoreDeviceTunnelProxy':
|
|
947
|
+
return cls(await lockdown.aio_start_lockdown_service(cls.SERVICE_NAME), lockdown.udid)
|
|
948
|
+
|
|
949
|
+
def __init__(self, service: ServiceConnection, remote_identifier: str) -> None:
|
|
950
|
+
self._service: ServiceConnection = service
|
|
951
|
+
self._remote_identifier: str = remote_identifier
|
|
945
952
|
|
|
946
953
|
@property
|
|
947
954
|
def remote_identifier(self) -> str:
|
|
948
|
-
return self.
|
|
955
|
+
return self._remote_identifier
|
|
949
956
|
|
|
950
957
|
@asynccontextmanager
|
|
951
958
|
async def start_tcp_tunnel(self) -> AsyncGenerator['TunnelResult', None]:
|
|
952
|
-
self._service
|
|
959
|
+
assert self._service is not None, 'service must be connected first'
|
|
953
960
|
tunnel = RemotePairingTcpTunnel(self._service.reader, self._service.writer)
|
|
954
961
|
handshake_response = await tunnel.request_tunnel_establish()
|
|
955
962
|
tunnel.start_tunnel(handshake_response['clientParameters']['address'],
|
|
@@ -1078,13 +1085,14 @@ async def get_remote_pairing_tunnel_services(
|
|
|
1078
1085
|
udid: Optional[str] = None) -> list[RemotePairingTunnelService]:
|
|
1079
1086
|
result = []
|
|
1080
1087
|
for answer in await browse_remotepairing(timeout=bonjour_timeout):
|
|
1081
|
-
for
|
|
1088
|
+
for address in answer.addresses:
|
|
1082
1089
|
for identifier in iter_remote_paired_identifiers():
|
|
1083
1090
|
if udid is not None and identifier != udid:
|
|
1084
1091
|
continue
|
|
1085
1092
|
conn = None
|
|
1086
1093
|
try:
|
|
1087
|
-
conn = await create_core_device_tunnel_service_using_remotepairing(
|
|
1094
|
+
conn = await create_core_device_tunnel_service_using_remotepairing(
|
|
1095
|
+
identifier, address.full_ip, answer.port)
|
|
1088
1096
|
result.append(conn)
|
|
1089
1097
|
break
|
|
1090
1098
|
except ConnectionAbortedError:
|
pymobiledevice3/remote/utils.py
CHANGED
|
@@ -17,8 +17,8 @@ async def get_rsds(bonjour_timeout: float = DEFAULT_BONJOUR_TIMEOUT, udid: Optio
|
|
|
17
17
|
result = []
|
|
18
18
|
with stop_remoted():
|
|
19
19
|
for answer in await browse_remoted(timeout=bonjour_timeout):
|
|
20
|
-
for
|
|
21
|
-
rsd = RemoteServiceDiscoveryService((
|
|
20
|
+
for address in answer.addresses:
|
|
21
|
+
rsd = RemoteServiceDiscoveryService((address.full_ip, RSD_PORT))
|
|
22
22
|
try:
|
|
23
23
|
await rsd.connect()
|
|
24
24
|
except ConnectionRefusedError:
|
|
@@ -100,10 +100,24 @@ kcdata_types = {
|
|
|
100
100
|
'STACKSHOT_KCTYPE_LATENCY_INFO_THREAD': 0x92d,
|
|
101
101
|
'STACKSHOT_KCTYPE_LOADINFO64_TEXT_EXEC': 0x92e,
|
|
102
102
|
|
|
103
|
+
'STACKSHOT_KCTYPE_USER_ASYNC_START_INDEX': 0x932,
|
|
104
|
+
'STACKSHOT_KCTYPE_USER_ASYNC_STACKLR64': 0x933,
|
|
105
|
+
|
|
103
106
|
'STACKSHOT_KCTYPE_TASK_DELTA_SNAPSHOT': 0x940,
|
|
104
107
|
'STACKSHOT_KCTYPE_THREAD_DELTA_SNAPSHOT': 0x941,
|
|
105
|
-
'
|
|
108
|
+
'STACKSHOT_KCCONTAINER_SHAREDCACHE': 0x942,
|
|
106
109
|
'STACKSHOT_KCTYPE_UNKNOWN_0x943': 0x943,
|
|
110
|
+
'STACKSHOT_KCCONTAINER_EXCLAVES': 0x949,
|
|
111
|
+
'STACKSHOT_KCCONTAINER_EXCLAVE_SCRESULT': 0x94a,
|
|
112
|
+
|
|
113
|
+
'STACKSHOT_KCCONTAINER_EXCLAVE_IPCSTACKENTRY': 0x94c,
|
|
114
|
+
'STACKSHOT_KCTYPE_EXCLAVE_IPCSTACKENTRY_INFO': 0x94d,
|
|
115
|
+
'STACKSHOT_KCTYPE_EXCLAVE_IPCSTACKENTRY_ECSTACK': 0x94e,
|
|
116
|
+
'STACKSHOT_KCCONTAINER_EXCLAVE_ADDRESSSPACE': 0x94f,
|
|
117
|
+
|
|
118
|
+
'STACKSHOT_KCCONTAINER_EXCLAVE_TEXTLAYOUT': 0x952,
|
|
119
|
+
'STACKSHOT_KCTYPE_EXCLAVE_TEXTLAYOUT_INFO': 0x953,
|
|
120
|
+
'STACKSHOT_KCTYPE_EXCLAVE_TEXTLAYOUT_SEGMENTS': 0x954,
|
|
107
121
|
|
|
108
122
|
'KCDATA_TYPE_BUFFER_END': 0xF19158ED,
|
|
109
123
|
|
|
@@ -185,6 +199,16 @@ predefined_names = {
|
|
|
185
199
|
kcdata_types_enum.STACKSHOT_KCTYPE_STACKSHOT_FAULT_STATS: 'stackshot_fault_stats',
|
|
186
200
|
kcdata_types_enum.STACKSHOT_KCTYPE_STACKSHOT_DURATION: 'stackshot_duration',
|
|
187
201
|
kcdata_types_enum.STACKSHOT_KCTYPE_LOADINFO64_TEXT_EXEC: 'dyld_load_info_text_exec',
|
|
202
|
+
kcdata_types_enum.STACKSHOT_KCTYPE_USER_ASYNC_STACKLR64: 'user_async_stack_lr',
|
|
203
|
+
kcdata_types_enum.STACKSHOT_KCCONTAINER_SHAREDCACHE: 'container_sharedcache',
|
|
204
|
+
kcdata_types_enum.STACKSHOT_KCCONTAINER_EXCLAVES: 'container_exclaves',
|
|
205
|
+
kcdata_types_enum.STACKSHOT_KCCONTAINER_EXCLAVE_SCRESULT: 'exclave_scresult',
|
|
206
|
+
kcdata_types_enum.STACKSHOT_KCCONTAINER_EXCLAVE_IPCSTACKENTRY: 'exclave_ipc_stack_entry',
|
|
207
|
+
kcdata_types_enum.STACKSHOT_KCTYPE_EXCLAVE_IPCSTACKENTRY_ECSTACK: 'exclave_ipc_stack_entry_ecstack',
|
|
208
|
+
kcdata_types_enum.STACKSHOT_KCCONTAINER_EXCLAVE_ADDRESSSPACE: 'exclave_address_space',
|
|
209
|
+
kcdata_types_enum.STACKSHOT_KCCONTAINER_EXCLAVE_TEXTLAYOUT: 'exclave_text_layout',
|
|
210
|
+
kcdata_types_enum.STACKSHOT_KCTYPE_EXCLAVE_TEXTLAYOUT_INFO: 'exclave_text_layout_info',
|
|
211
|
+
kcdata_types_enum.STACKSHOT_KCTYPE_EXCLAVE_TEXTLAYOUT_SEGMENTS: 'exclave_text_layout_segments',
|
|
188
212
|
}
|
|
189
213
|
|
|
190
214
|
predefined_name_substruct = 'name' / Computed(lambda ctx: predefined_names[ctx._.type])
|
|
@@ -443,6 +467,39 @@ loadinfo64_text_exec = Struct(
|
|
|
443
467
|
),
|
|
444
468
|
)
|
|
445
469
|
|
|
470
|
+
user_async_stacklr64 = Struct(
|
|
471
|
+
predefined_name_substruct,
|
|
472
|
+
'obj' / Struct(
|
|
473
|
+
'lr' / Int64ul,
|
|
474
|
+
),
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
exclave_ipcstackentry_ecstack = Struct(
|
|
478
|
+
predefined_name_substruct,
|
|
479
|
+
'obj' / Struct(
|
|
480
|
+
'addr' / Int64ul,
|
|
481
|
+
),
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
exclave_textlayout_info = Struct(
|
|
485
|
+
predefined_name_substruct,
|
|
486
|
+
'obj' / Struct(
|
|
487
|
+
'layout_id' / Int64ul,
|
|
488
|
+
'etl_flags' / Int64ul,
|
|
489
|
+
'sharedcache_index' / Int32ul,
|
|
490
|
+
),
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
exclave_textlayout_segments = Struct(
|
|
494
|
+
predefined_name_substruct,
|
|
495
|
+
'obj' / Struct(
|
|
496
|
+
'_imageUUID' / Bytes(16),
|
|
497
|
+
'imageUUID' / Computed(lambda ctx: uuid.UUID(bytes=ctx._imageUUID)),
|
|
498
|
+
'layoutSegment_loadAddress' / Int64ul,
|
|
499
|
+
'layoutSegment_rawLoadAddress' / Int64ul,
|
|
500
|
+
),
|
|
501
|
+
)
|
|
502
|
+
|
|
446
503
|
kcdata_types_structures = {
|
|
447
504
|
kcdata_types_enum.KCDATA_TYPE_UINT32_DESC: uint32_desc,
|
|
448
505
|
kcdata_types_enum.KCDATA_TYPE_UINT64_DESC: uint64_desc,
|
|
@@ -482,6 +539,10 @@ kcdata_types_structures = {
|
|
|
482
539
|
kcdata_types_enum.KCDATA_TYPE_BUFFER_END: Pass,
|
|
483
540
|
kcdata_types_enum.STACKSHOT_KCTYPE_STACKSHOT_DURATION: stackshot_duration,
|
|
484
541
|
kcdata_types_enum.STACKSHOT_KCTYPE_LOADINFO64_TEXT_EXEC: loadinfo64_text_exec,
|
|
542
|
+
kcdata_types_enum.STACKSHOT_KCTYPE_USER_ASYNC_STACKLR64: user_async_stacklr64,
|
|
543
|
+
kcdata_types_enum.STACKSHOT_KCTYPE_EXCLAVE_IPCSTACKENTRY_ECSTACK: exclave_ipcstackentry_ecstack,
|
|
544
|
+
kcdata_types_enum.STACKSHOT_KCTYPE_EXCLAVE_TEXTLAYOUT_INFO: exclave_textlayout_info,
|
|
545
|
+
kcdata_types_enum.STACKSHOT_KCTYPE_EXCLAVE_TEXTLAYOUT_SEGMENTS: exclave_textlayout_segments,
|
|
485
546
|
}
|
|
486
547
|
|
|
487
548
|
kcdata_item = Struct(
|
|
@@ -7,10 +7,13 @@ import signal
|
|
|
7
7
|
import traceback
|
|
8
8
|
import warnings
|
|
9
9
|
from contextlib import asynccontextmanager, suppress
|
|
10
|
+
from ssl import SSLEOFError
|
|
10
11
|
from typing import Optional, Union
|
|
11
12
|
|
|
12
13
|
import construct
|
|
13
14
|
|
|
15
|
+
from pymobiledevice3.bonjour import browse_remoted
|
|
16
|
+
|
|
14
17
|
with warnings.catch_warnings():
|
|
15
18
|
# Ignore: "Core Pydantic V1 functionality isn't compatible with Python 3.14 or greater."
|
|
16
19
|
warnings.simplefilter('ignore', category=UserWarning)
|
|
@@ -22,9 +25,9 @@ from fastapi import FastAPI
|
|
|
22
25
|
from packaging.version import Version
|
|
23
26
|
|
|
24
27
|
from pymobiledevice3 import usbmux
|
|
25
|
-
from pymobiledevice3.bonjour import REMOTED_SERVICE_NAMES, browse
|
|
26
28
|
from pymobiledevice3.exceptions import ConnectionFailedError, ConnectionFailedToUsbmuxdError, DeviceNotFoundError, \
|
|
27
|
-
GetProhibitedError, IncorrectModeError, InvalidServiceError, LockdownError, MuxException, PairingError
|
|
29
|
+
GetProhibitedError, IncorrectModeError, InvalidServiceError, LockdownError, MuxException, PairingError, \
|
|
30
|
+
StreamClosedError
|
|
28
31
|
from pymobiledevice3.lockdown import create_using_usbmux, get_mobdev2_lockdowns
|
|
29
32
|
from pymobiledevice3.osu.os_utils import get_os_utils
|
|
30
33
|
from pymobiledevice3.remote.common import TunnelProtocol
|
|
@@ -42,7 +45,7 @@ REATTEMPT_INTERVAL = 5
|
|
|
42
45
|
REATTEMPT_COUNT = 5
|
|
43
46
|
|
|
44
47
|
REMOTEPAIRING_INTERVAL = 5
|
|
45
|
-
|
|
48
|
+
MOBDEV2_INTERVAL = 5
|
|
46
49
|
|
|
47
50
|
USBMUX_INTERVAL = 2
|
|
48
51
|
OSUTILS = get_os_utils()
|
|
@@ -91,50 +94,75 @@ class TunneldCore:
|
|
|
91
94
|
|
|
92
95
|
@asyncio_print_traceback
|
|
93
96
|
async def monitor_usb_task(self) -> None:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
self.tunnel_tasks
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
97
|
+
try:
|
|
98
|
+
previous_ips = []
|
|
99
|
+
while True:
|
|
100
|
+
current_ips = OSUTILS.get_ipv6_ips()
|
|
101
|
+
added = [ip for ip in current_ips if ip not in previous_ips]
|
|
102
|
+
removed = [ip for ip in previous_ips if ip not in current_ips]
|
|
103
|
+
|
|
104
|
+
previous_ips = current_ips
|
|
105
|
+
|
|
106
|
+
# logger.debug(f'added interfaces: {added}')
|
|
107
|
+
# logger.debug(f'removed interfaces: {removed}')
|
|
108
|
+
|
|
109
|
+
for ip in removed:
|
|
110
|
+
if ip in self.tunnel_tasks:
|
|
111
|
+
self.tunnel_tasks[ip].task.cancel()
|
|
112
|
+
with suppress(asyncio.CancelledError):
|
|
113
|
+
await self.tunnel_tasks[ip].task
|
|
114
|
+
|
|
115
|
+
if added:
|
|
116
|
+
# A new interface was attached
|
|
117
|
+
for answer in await browse_remoted():
|
|
118
|
+
for address in answer.addresses:
|
|
119
|
+
if address.iface.startswith('utun'):
|
|
120
|
+
# Skip already established tunnels
|
|
121
|
+
continue
|
|
122
|
+
if address.full_ip in self.tunnel_tasks.keys():
|
|
123
|
+
# Skip already established tunnels
|
|
124
|
+
continue
|
|
125
|
+
self.tunnel_tasks[address.full_ip] = TunnelTask(
|
|
126
|
+
task=asyncio.create_task(
|
|
127
|
+
self.handle_new_potential_usb_cdc_ncm_interface_task(address.full_ip),
|
|
128
|
+
name=f'handle-new-potential-usb-cdc-ncm-interface-task-{address.full_ip}'))
|
|
129
|
+
|
|
130
|
+
# wait before re-iterating
|
|
131
|
+
await asyncio.sleep(1)
|
|
132
|
+
except asyncio.CancelledError:
|
|
133
|
+
pass
|
|
117
134
|
|
|
118
135
|
@asyncio_print_traceback
|
|
119
136
|
async def monitor_wifi_task(self) -> None:
|
|
120
137
|
try:
|
|
121
138
|
while True:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
139
|
+
try:
|
|
140
|
+
remote_pairing_tunnel_services = await get_remote_pairing_tunnel_services()
|
|
141
|
+
for service in remote_pairing_tunnel_services:
|
|
142
|
+
if service.hostname in self.tunnel_tasks:
|
|
143
|
+
# skip tunnel if already exists for this ip
|
|
144
|
+
await service.close()
|
|
145
|
+
continue
|
|
146
|
+
if self.tunnel_exists_for_udid(service.remote_identifier):
|
|
147
|
+
# skip tunnel if already exists for this udid
|
|
148
|
+
await service.close()
|
|
149
|
+
continue
|
|
150
|
+
self.tunnel_tasks[service.hostname] = TunnelTask(
|
|
151
|
+
task=asyncio.create_task(self.start_tunnel_task(service.hostname, service),
|
|
152
|
+
name=f'start-tunnel-task-wifi-{service.hostname}'),
|
|
153
|
+
udid=service.remote_identifier
|
|
154
|
+
)
|
|
155
|
+
except asyncio.exceptions.IncompleteReadError:
|
|
156
|
+
continue
|
|
157
|
+
except asyncio.CancelledError:
|
|
158
|
+
# Raise and cancel gracefully
|
|
159
|
+
raise
|
|
160
|
+
except Exception:
|
|
161
|
+
logger.error(f'Got exception from {asyncio.current_task().get_name()}: {traceback.format_exc()}')
|
|
162
|
+
continue
|
|
136
163
|
await asyncio.sleep(REMOTEPAIRING_INTERVAL)
|
|
137
164
|
except asyncio.CancelledError:
|
|
165
|
+
# Cancel gracefully
|
|
138
166
|
pass
|
|
139
167
|
|
|
140
168
|
@asyncio_print_traceback
|
|
@@ -145,11 +173,20 @@ class TunneldCore:
|
|
|
145
173
|
for mux_device in usbmux.list_devices():
|
|
146
174
|
task_identifier = f'usbmux-{mux_device.serial}-{mux_device.connection_type}'
|
|
147
175
|
if self.tunnel_exists_for_udid(mux_device.serial):
|
|
176
|
+
# Skip if already established a tunnel for this udid
|
|
148
177
|
continue
|
|
178
|
+
if task_identifier in self.tunnel_tasks:
|
|
179
|
+
# Skip if already trying to establish a tunnel for this device
|
|
180
|
+
continue
|
|
181
|
+
service = None
|
|
149
182
|
try:
|
|
150
|
-
|
|
183
|
+
with create_using_usbmux(mux_device.serial) as lockdown:
|
|
184
|
+
service = await CoreDeviceTunnelProxy.create(lockdown)
|
|
151
185
|
except (MuxException, InvalidServiceError, GetProhibitedError, construct.core.StreamError,
|
|
152
|
-
ConnectionAbortedError, DeviceNotFoundError, LockdownError, IncorrectModeError
|
|
186
|
+
ConnectionAbortedError, DeviceNotFoundError, LockdownError, IncorrectModeError,
|
|
187
|
+
SSLEOFError):
|
|
188
|
+
if service is not None:
|
|
189
|
+
await service.close()
|
|
153
190
|
continue
|
|
154
191
|
self.tunnel_tasks[task_identifier] = TunnelTask(
|
|
155
192
|
udid=mux_device.serial,
|
|
@@ -172,22 +209,26 @@ class TunneldCore:
|
|
|
172
209
|
try:
|
|
173
210
|
while True:
|
|
174
211
|
async for ip, lockdown in get_mobdev2_lockdowns(only_paired=True):
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
212
|
+
with lockdown:
|
|
213
|
+
udid = lockdown.udid
|
|
214
|
+
task_identifier = f'mobdev2-{udid}-{ip}'
|
|
215
|
+
if self.tunnel_exists_for_udid(udid):
|
|
216
|
+
# Skip tunnel if already exists for this udid
|
|
217
|
+
continue
|
|
218
|
+
if task_identifier in self.tunnel_tasks:
|
|
219
|
+
# Skip if already trying to establish a tunnel for this device
|
|
220
|
+
continue
|
|
221
|
+
try:
|
|
222
|
+
tunnel_service = await CoreDeviceTunnelProxy.create(lockdown)
|
|
223
|
+
except InvalidServiceError:
|
|
224
|
+
logger.warning(f'[{task_identifier}] failed to start CoreDeviceTunnelProxy - skipping')
|
|
225
|
+
continue
|
|
185
226
|
self.tunnel_tasks[task_identifier] = TunnelTask(
|
|
186
227
|
task=asyncio.create_task(self.start_tunnel_task(task_identifier, tunnel_service),
|
|
187
228
|
name=f'start-tunnel-task-{task_identifier}'),
|
|
188
|
-
udid=
|
|
229
|
+
udid=udid
|
|
189
230
|
)
|
|
190
|
-
await asyncio.sleep(
|
|
231
|
+
await asyncio.sleep(MOBDEV2_INTERVAL)
|
|
191
232
|
except asyncio.CancelledError:
|
|
192
233
|
pass
|
|
193
234
|
|
|
@@ -219,8 +260,8 @@ class TunneldCore:
|
|
|
219
260
|
else:
|
|
220
261
|
bailed_out = True
|
|
221
262
|
logger.debug(
|
|
222
|
-
f'
|
|
223
|
-
f'
|
|
263
|
+
f'[{asyncio.current_task().get_name()}] Not establishing tunnel since there is already an '
|
|
264
|
+
f'active one for same udid')
|
|
224
265
|
except asyncio.CancelledError:
|
|
225
266
|
pass
|
|
226
267
|
except (asyncio.exceptions.IncompleteReadError, TimeoutError, OSError, ConnectionResetError, StreamError,
|
|
@@ -254,30 +295,28 @@ class TunneldCore:
|
|
|
254
295
|
async def handle_new_potential_usb_cdc_ncm_interface_task(self, ip: str) -> None:
|
|
255
296
|
rsd = None
|
|
256
297
|
try:
|
|
257
|
-
answers = None
|
|
258
|
-
for i in range(REATTEMPT_COUNT):
|
|
259
|
-
answers = await browse(REMOTED_SERVICE_NAMES, [ip])
|
|
260
|
-
if answers:
|
|
261
|
-
break
|
|
262
|
-
logger.debug(f'No addresses found for: {ip}')
|
|
263
|
-
await asyncio.sleep(REATTEMPT_INTERVAL)
|
|
264
|
-
|
|
265
|
-
if not answers:
|
|
266
|
-
raise asyncio.CancelledError()
|
|
267
|
-
|
|
268
|
-
peer_address = answers[0].ips[0]
|
|
269
|
-
|
|
270
298
|
# establish an untrusted RSD handshake
|
|
271
|
-
rsd = RemoteServiceDiscoveryService((
|
|
299
|
+
rsd = RemoteServiceDiscoveryService((ip, RSD_PORT))
|
|
272
300
|
|
|
273
301
|
with stop_remoted():
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
302
|
+
first_time = True
|
|
303
|
+
retry = False
|
|
304
|
+
while retry or first_time:
|
|
305
|
+
retry = False
|
|
306
|
+
try:
|
|
307
|
+
await rsd.connect()
|
|
308
|
+
except StreamClosedError:
|
|
309
|
+
# Could be on first try because of remoted race
|
|
310
|
+
if first_time:
|
|
311
|
+
retry = True
|
|
312
|
+
except (ConnectionRefusedError, TimeoutError, OSError):
|
|
313
|
+
raise asyncio.CancelledError()
|
|
314
|
+
finally:
|
|
315
|
+
first_time = False
|
|
278
316
|
|
|
279
317
|
if (self.protocol == TunnelProtocol.QUIC) and (Version(rsd.product_version) < Version('17.0.0')):
|
|
280
318
|
await rsd.close()
|
|
319
|
+
rsd = None
|
|
281
320
|
raise asyncio.CancelledError()
|
|
282
321
|
|
|
283
322
|
await asyncio.create_task(
|
|
@@ -299,7 +338,7 @@ class TunneldCore:
|
|
|
299
338
|
pass
|
|
300
339
|
|
|
301
340
|
if ip in self.tunnel_tasks:
|
|
302
|
-
#
|
|
341
|
+
# In case the tunnel was removed just now
|
|
303
342
|
self.tunnel_tasks.pop(ip)
|
|
304
343
|
|
|
305
344
|
async def close(self) -> None:
|
|
@@ -325,7 +364,7 @@ class TunneldCore:
|
|
|
325
364
|
""" Cancel active tunnels """
|
|
326
365
|
for tunnel_ip in self.get_tunnels_ips().get(udid, []):
|
|
327
366
|
self.tunnel_tasks.pop(tunnel_ip).task.cancel()
|
|
328
|
-
logger.info(f'
|
|
367
|
+
logger.info(f'Canceling tunnel {tunnel_ip}')
|
|
329
368
|
|
|
330
369
|
def clear(self) -> None:
|
|
331
370
|
""" Clear active tunnels """
|
|
@@ -348,7 +387,6 @@ class TunneldRunner:
|
|
|
348
387
|
wifi_monitor: bool = True, usbmux_monitor: bool = True, mobdev2_monitor: bool = True):
|
|
349
388
|
@asynccontextmanager
|
|
350
389
|
async def lifespan(app: FastAPI):
|
|
351
|
-
logging.getLogger('zeroconf').disabled = True
|
|
352
390
|
self._tunneld_core.start()
|
|
353
391
|
yield
|
|
354
392
|
logger.info('Closing tunneld tasks...')
|
|
@@ -427,7 +465,8 @@ class TunneldRunner:
|
|
|
427
465
|
if not created_task and connection_type in ('usbmux', None):
|
|
428
466
|
task_identifier = f'usbmux-{udid}'
|
|
429
467
|
try:
|
|
430
|
-
|
|
468
|
+
with create_using_usbmux(udid) as lockdown:
|
|
469
|
+
service = await CoreDeviceTunnelProxy.create(lockdown)
|
|
431
470
|
task = asyncio.create_task(
|
|
432
471
|
self._tunneld_core.start_tunnel_task(task_identifier, service, protocol=TunnelProtocol.TCP,
|
|
433
472
|
queue=queue),
|
|
@@ -469,7 +508,7 @@ class TunneldRunner:
|
|
|
469
508
|
}}))
|
|
470
509
|
|
|
471
510
|
if not created_task:
|
|
472
|
-
return fastapi.Response(status_code=501, content=json.dumps({'error': 'task not
|
|
511
|
+
return fastapi.Response(status_code=501, content=json.dumps({'error': 'task not created'}))
|
|
473
512
|
|
|
474
513
|
tunnel: Optional[TunnelResult] = await queue.get()
|
|
475
514
|
if tunnel is not None:
|
pymobiledevice3/usbmux.py
CHANGED
|
@@ -174,15 +174,17 @@ class MuxConnection:
|
|
|
174
174
|
# first attempt to connect with possibly the wrong version header (plist protocol)
|
|
175
175
|
sock = MuxConnection.create_usbmux_socket(usbmux_address=usbmux_address)
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
177
|
+
try:
|
|
178
|
+
message = usbmuxd_request.build({
|
|
179
|
+
'header': {'version': usbmuxd_version.PLIST, 'message': usbmuxd_msgtype.PLIST, 'tag': 1},
|
|
180
|
+
'data': plistlib.dumps({'MessageType': 'ReadBUID'})
|
|
181
|
+
})
|
|
182
|
+
sock.send(message)
|
|
183
|
+
response = usbmuxd_response.parse_stream(sock)
|
|
184
|
+
|
|
185
|
+
finally:
|
|
186
|
+
# If we sent a bad request, we should re-create the socket in the correct version this time
|
|
187
|
+
sock.close()
|
|
186
188
|
sock = MuxConnection.create_usbmux_socket(usbmux_address=usbmux_address)
|
|
187
189
|
|
|
188
190
|
if response.header.version == usbmuxd_version.BINARY:
|
|
@@ -414,9 +416,11 @@ def create_mux(usbmux_address: Optional[str] = None) -> MuxConnection:
|
|
|
414
416
|
|
|
415
417
|
def list_devices(usbmux_address: Optional[str] = None) -> list[MuxDevice]:
|
|
416
418
|
mux = create_mux(usbmux_address=usbmux_address)
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
419
|
+
try:
|
|
420
|
+
mux.get_device_list(0.1)
|
|
421
|
+
devices = mux.devices
|
|
422
|
+
finally:
|
|
423
|
+
mux.close()
|
|
420
424
|
return devices
|
|
421
425
|
|
|
422
426
|
|
pymobiledevice3/utils.py
CHANGED
|
@@ -45,7 +45,7 @@ def asyncio_print_traceback(f: Callable):
|
|
|
45
45
|
async def wrapper(*args, **kwargs):
|
|
46
46
|
try:
|
|
47
47
|
return await f(*args, **kwargs)
|
|
48
|
-
except Exception as e: # noqa: E72
|
|
48
|
+
except (Exception, RuntimeError) as e: # noqa: E72
|
|
49
49
|
if not isinstance(e, asyncio.CancelledError):
|
|
50
50
|
traceback.print_exc()
|
|
51
51
|
raise
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pymobiledevice3
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.1
|
|
4
4
|
Summary: Pure python3 implementation for working with iDevices (iPhone, etc...)
|
|
5
5
|
Author-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
|
|
6
6
|
Maintainer-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
|
|
@@ -47,7 +47,6 @@ Requires-Dist: nest_asyncio>=1.5.5
|
|
|
47
47
|
Requires-Dist: Pillow
|
|
48
48
|
Requires-Dist: inquirer3>=0.6.0
|
|
49
49
|
Requires-Dist: ipsw_parser>=1.3.4
|
|
50
|
-
Requires-Dist: zeroconf>=0.132.2
|
|
51
50
|
Requires-Dist: ifaddr
|
|
52
51
|
Requires-Dist: hyperframe
|
|
53
52
|
Requires-Dist: srptools
|
|
@@ -7,35 +7,35 @@ misc/remotexpc_sniffer.py,sha256=EThsKN0Vbs-mnLKCDXeooqg0MdpSkjwhAZHZwvhI458,797
|
|
|
7
7
|
misc/understanding_idevice_protocol_layers.md,sha256=8tEqRXWOUPoxOJLZVh7C7H9JGCh2sQ3B5UH8_AymaQc,18805
|
|
8
8
|
misc/usbmux_sniff.sh,sha256=iWtbucOEQ9_UEFXk9x-2VNt48Jg5zrPsnUbZ_LfZxwA,212
|
|
9
9
|
pymobiledevice3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
pymobiledevice3/__main__.py,sha256=
|
|
11
|
-
pymobiledevice3/_version.py,sha256=
|
|
12
|
-
pymobiledevice3/bonjour.py,sha256
|
|
10
|
+
pymobiledevice3/__main__.py,sha256=viUbhGzaoDi18zu5crX33PMws4qNBjBTwnaK5rAaObY,11651
|
|
11
|
+
pymobiledevice3/_version.py,sha256=MpPN0ZsEisc17aqn7bpe-bL3iBKNBaREenP6NUe6-4s,704
|
|
12
|
+
pymobiledevice3/bonjour.py,sha256=_f5RQs9uLDjDFsXMvykQs8vFoGJXiw6xfth86qewEGA,13719
|
|
13
13
|
pymobiledevice3/ca.py,sha256=mTvWdSjTZw6Eb-22-IZ323GyA1G6CXYmdPedImTjm3A,10542
|
|
14
14
|
pymobiledevice3/common.py,sha256=-PG6oaUkNFlB3jb7E0finMrX8wqhkS-cuTAfmLvZUmc,329
|
|
15
15
|
pymobiledevice3/exceptions.py,sha256=VqWB6WWoMrXt8GDdKqRHeJ1otP-eZIThoHERswXWqpw,10347
|
|
16
16
|
pymobiledevice3/irecv.py,sha256=FoEln1_zHkAiNcEctB5bStfhKNgniOSg7lg9xcX1U2Q,10596
|
|
17
17
|
pymobiledevice3/irecv_devices.py,sha256=BG30ecXSChxdyYCCGIrIO0sVWT31hbKymB78nZWVfWc,38506
|
|
18
|
-
pymobiledevice3/lockdown.py,sha256=
|
|
18
|
+
pymobiledevice3/lockdown.py,sha256=jVrw--ifD8ewGwp5fZVYdoQDuaUiSckZ7fnB8dDtc58,38615
|
|
19
19
|
pymobiledevice3/lockdown_service_provider.py,sha256=l5N72tiuI-2uowk8wu6B7qkjY2UmqQsnhdJqvJy3I8A,1744
|
|
20
20
|
pymobiledevice3/pair_records.py,sha256=Tr28mlBWPXvOF7vdKBDOuw1rCRwm6RViDTGbikfP77I,6034
|
|
21
21
|
pymobiledevice3/service_connection.py,sha256=_-PTLFr3krtwEBNHEKXCd_2eOGwMpbsfPbB8AX2uN-g,14861
|
|
22
22
|
pymobiledevice3/tcp_forwarder.py,sha256=TVtIHn4hFlNIMEYXW9nwdSEhLfHaEHf4jkMsfJXLrTA,8906
|
|
23
|
-
pymobiledevice3/usbmux.py,sha256=
|
|
24
|
-
pymobiledevice3/utils.py,sha256=
|
|
23
|
+
pymobiledevice3/usbmux.py,sha256=NSgcgEbaFxGqyyYuZm4SgLx1DPdkP1oeGRLZ8bEkXes,16916
|
|
24
|
+
pymobiledevice3/utils.py,sha256=X3hU3wf_REUPRS-XtLgGgI2pxIwDGZP0RpZvZf7hOBY,2195
|
|
25
25
|
pymobiledevice3/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
26
|
pymobiledevice3/cli/activation.py,sha256=mF64abX7d0bbyALVC-f_9rjc1DuB6mukP3Zwg9Hoj1Y,1321
|
|
27
27
|
pymobiledevice3/cli/afc.py,sha256=z-qnBVUPA4uOnXADkYVyRJxeibRDFF3j5LejHt_6UW4,2129
|
|
28
28
|
pymobiledevice3/cli/amfi.py,sha256=6hlqKrKOFj0secUnLQ8grDDnnh3fRsO6x_vo40oy22w,963
|
|
29
29
|
pymobiledevice3/cli/apps.py,sha256=LH75A1gDRGP0nWO4QFcOUDg0EdphkGuYnWJgIQHrIBg,3859
|
|
30
30
|
pymobiledevice3/cli/backup.py,sha256=SyHojiRRguxdkAPMz_Rp_9-zJNeuOtmpa0imdPN12-4,6691
|
|
31
|
-
pymobiledevice3/cli/bonjour.py,sha256=
|
|
31
|
+
pymobiledevice3/cli/bonjour.py,sha256=X5W-5rPLX3xAwEeQJLPB_iOhdHcOL9ePkrm3xx4-Qic,2854
|
|
32
32
|
pymobiledevice3/cli/cli_common.py,sha256=lQFhkTwPfi1UYFkMiDc-jrId2s2yHwzF5lFTK0dXM_s,12945
|
|
33
33
|
pymobiledevice3/cli/companion_proxy.py,sha256=ey0X3moJ49zVJoNCpRMMHmf9fBZfdqimhz2VCA35oII,581
|
|
34
34
|
pymobiledevice3/cli/completions.py,sha256=t8oryezQTcWDno_E2Cch7o1f-qURVL9M1Z4o6uLA_kM,1722
|
|
35
35
|
pymobiledevice3/cli/crash.py,sha256=m1vs0_KUy4cxu8vHYjn7olay8oPQGTFZqMCHspnGpVs,3181
|
|
36
36
|
pymobiledevice3/cli/developer.py,sha256=qSGvUZPVCwJZQaE9jF2vk-0Fp3x_2-wIlFjy-QoM5cI,61471
|
|
37
37
|
pymobiledevice3/cli/diagnostics.py,sha256=VDWr41ryIZcpuQp47nQSzCiSuIILExqGSrwFizXCIkI,3641
|
|
38
|
-
pymobiledevice3/cli/lockdown.py,sha256=
|
|
38
|
+
pymobiledevice3/cli/lockdown.py,sha256=AV_7snLEkc9mbwWVlWg1Ki0tWQMtPHReziun_lxjNvY,7133
|
|
39
39
|
pymobiledevice3/cli/mounter.py,sha256=AnNneNF_kW7XnBMe4V5cvlbLYd_mAP4tuB3PXLQpeiA,7724
|
|
40
40
|
pymobiledevice3/cli/notification.py,sha256=vqn8uPslr7A9HiZ4yrs7YKF2VLS7Nk4G7ab5ELljpVQ,1962
|
|
41
41
|
pymobiledevice3/cli/pcap.py,sha256=KzFxXWFRYWNOEJE1XAuMF2cG8I4k5wFVcMRhSdY4GQg,2188
|
|
@@ -43,7 +43,7 @@ pymobiledevice3/cli/power_assertion.py,sha256=aTlesowRyrbd9JXebEZe9SomTkDkZaAXIO
|
|
|
43
43
|
pymobiledevice3/cli/processes.py,sha256=XNJe2KaacP7c-1NtR_HF6Gd5rByyj3vpyyT_xEntIbA,1102
|
|
44
44
|
pymobiledevice3/cli/profile.py,sha256=WT8hgYOmkOHUlWtEz-BoBelCerT70TpwBBzJC8wRLmY,7939
|
|
45
45
|
pymobiledevice3/cli/provision.py,sha256=yWabJrieISrBfFo7vCFIAM8xXLG-8_9qRe1igkTHIA0,1967
|
|
46
|
-
pymobiledevice3/cli/remote.py,sha256=
|
|
46
|
+
pymobiledevice3/cli/remote.py,sha256=n_PbPnUqnQRYxVG-y8rERmWu__64OY0mDx12mX62pRg,11999
|
|
47
47
|
pymobiledevice3/cli/restore.py,sha256=vg3yjKOjsONUUzPp-XHIjNGMV8qCfptJrz6eJzG2diY,8172
|
|
48
48
|
pymobiledevice3/cli/springboard.py,sha256=pYMqnD0zN_ETIASPqxBohi54F2HMC9jCILW4epVaaIk,3140
|
|
49
49
|
pymobiledevice3/cli/syslog.py,sha256=JfLhjyVAeRx16VC4BsAu308rABjcIQX8DB7vbVKGiic,7462
|
|
@@ -60,8 +60,8 @@ pymobiledevice3/remote/module_imports.py,sha256=DwExSL1r4kkFIWmXiQpqPo-cGl4duYd3
|
|
|
60
60
|
pymobiledevice3/remote/remote_service.py,sha256=fCyzm4oT_WEorAXVHVLYnIOyTOuMGhX69Co3HkUdRYY,867
|
|
61
61
|
pymobiledevice3/remote/remote_service_discovery.py,sha256=iqPE1PiDDB2ISK-ThuUPEiSU9ETZ-FGTcANhb6MrWmo,7156
|
|
62
62
|
pymobiledevice3/remote/remotexpc.py,sha256=KbFHaH4D3RnaATve6kaIpJMHNF8H-kdhbRbEbxFmO6w,8082
|
|
63
|
-
pymobiledevice3/remote/tunnel_service.py,sha256=
|
|
64
|
-
pymobiledevice3/remote/utils.py,sha256=
|
|
63
|
+
pymobiledevice3/remote/tunnel_service.py,sha256=LhPCduH4mNU8sACGI3aJeXpH0UjPfvsjm0FGA4QIV_4,46830
|
|
64
|
+
pymobiledevice3/remote/utils.py,sha256=PV9tICVY6-L7eoqRF1yNmOGxdC2kp9h0kBqeoIAo4pA,2530
|
|
65
65
|
pymobiledevice3/remote/xpc_message.py,sha256=-nVbf88ZN4ZNxLg6cOq4FfeKXYAoVRKnwGdfe7s-sZE,9336
|
|
66
66
|
pymobiledevice3/remote/core_device/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
67
|
pymobiledevice3/remote/core_device/app_service.py,sha256=x_K-3IA4NWG-skyffKyeGrS7slAbn3MsVLhdY62FSh0,5066
|
|
@@ -138,7 +138,7 @@ pymobiledevice3/services/dvt/instruments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5
|
|
|
138
138
|
pymobiledevice3/services/dvt/instruments/activity_trace_tap.py,sha256=SARFCaEhDxams9slRKZBBiZDCmR6WDlRdho3mxZGNLY,8769
|
|
139
139
|
pymobiledevice3/services/dvt/instruments/application_listing.py,sha256=6zWnYUyaxNFudrZo0oKGMTYG3T4sTm2w2XKi-jK-59E,666
|
|
140
140
|
pymobiledevice3/services/dvt/instruments/condition_inducer.py,sha256=u7CfKvS2dkL3xzbmU9e8zWY29RKRPuAqi3PT6REErpQ,1243
|
|
141
|
-
pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py,sha256=
|
|
141
|
+
pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py,sha256=XYV_b-LiryH7UEoeyIlMJ7UFVmRAabp6RT7RLJQ_57o,30499
|
|
142
142
|
pymobiledevice3/services/dvt/instruments/device_info.py,sha256=dP6PEW62MsRS8hUy3i000wwI2tE8jek_GvOmT1cjMUE,3085
|
|
143
143
|
pymobiledevice3/services/dvt/instruments/energy_monitor.py,sha256=ZzkoC1wyK1zYskiXDtUoEa9PPWnDM9WLl4tZWxfVl-k,892
|
|
144
144
|
pymobiledevice3/services/dvt/instruments/graphics.py,sha256=KjdO6o_rDsD1YfvW8rW2nOj0N7JfPblFTOlR9XIwwXY,588
|
|
@@ -164,10 +164,10 @@ pymobiledevice3/services/web_protocol/session_protocol.py,sha256=7dJkFyivu554K6I
|
|
|
164
164
|
pymobiledevice3/services/web_protocol/switch_to.py,sha256=hDddJUEePbRN-8xlllOeGhnYvE4NEnd8JJIlosLMB9c,2880
|
|
165
165
|
pymobiledevice3/tunneld/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
166
166
|
pymobiledevice3/tunneld/api.py,sha256=EfGKXEWhsMSB__menPmRmL9R6dpazVJDUy7B3pn05MM,2357
|
|
167
|
-
pymobiledevice3/tunneld/server.py,sha256=
|
|
168
|
-
pymobiledevice3-
|
|
169
|
-
pymobiledevice3-
|
|
170
|
-
pymobiledevice3-
|
|
171
|
-
pymobiledevice3-
|
|
172
|
-
pymobiledevice3-
|
|
173
|
-
pymobiledevice3-
|
|
167
|
+
pymobiledevice3/tunneld/server.py,sha256=fbwnKrm4d84MpopWKIWoQF1_ZEUB4vONKmHih6qce_U,25206
|
|
168
|
+
pymobiledevice3-5.0.1.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
|
169
|
+
pymobiledevice3-5.0.1.dist-info/METADATA,sha256=s8IX_VJnsx5y6tqtiY5JleNQhacBm6kN3eJVvur2GqY,17416
|
|
170
|
+
pymobiledevice3-5.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
171
|
+
pymobiledevice3-5.0.1.dist-info/entry_points.txt,sha256=jJMlOanHlVwUxcY__JwvKeWPrvBJr_wJyEq4oHIZNKE,66
|
|
172
|
+
pymobiledevice3-5.0.1.dist-info/top_level.txt,sha256=MjZoRqcWPOh5banG-BbDOnKEfsS3kCxqV9cv-nzyg2Q,21
|
|
173
|
+
pymobiledevice3-5.0.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|