pymobiledevice3 4.27.4__py3-none-any.whl → 5.1.2__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.
Files changed (143) hide show
  1. misc/plist_sniffer.py +15 -15
  2. misc/remotexpc_sniffer.py +29 -28
  3. pymobiledevice3/__main__.py +123 -98
  4. pymobiledevice3/_version.py +2 -2
  5. pymobiledevice3/bonjour.py +351 -117
  6. pymobiledevice3/ca.py +32 -24
  7. pymobiledevice3/cli/activation.py +7 -7
  8. pymobiledevice3/cli/afc.py +19 -19
  9. pymobiledevice3/cli/amfi.py +4 -4
  10. pymobiledevice3/cli/apps.py +51 -39
  11. pymobiledevice3/cli/backup.py +58 -32
  12. pymobiledevice3/cli/bonjour.py +27 -20
  13. pymobiledevice3/cli/cli_common.py +112 -81
  14. pymobiledevice3/cli/companion_proxy.py +4 -4
  15. pymobiledevice3/cli/completions.py +10 -10
  16. pymobiledevice3/cli/crash.py +37 -31
  17. pymobiledevice3/cli/developer.py +601 -519
  18. pymobiledevice3/cli/diagnostics.py +38 -33
  19. pymobiledevice3/cli/lockdown.py +82 -72
  20. pymobiledevice3/cli/mounter.py +84 -67
  21. pymobiledevice3/cli/notification.py +10 -10
  22. pymobiledevice3/cli/pcap.py +19 -14
  23. pymobiledevice3/cli/power_assertion.py +12 -10
  24. pymobiledevice3/cli/processes.py +10 -10
  25. pymobiledevice3/cli/profile.py +88 -77
  26. pymobiledevice3/cli/provision.py +17 -17
  27. pymobiledevice3/cli/remote.py +188 -111
  28. pymobiledevice3/cli/restore.py +43 -40
  29. pymobiledevice3/cli/springboard.py +30 -28
  30. pymobiledevice3/cli/syslog.py +85 -58
  31. pymobiledevice3/cli/usbmux.py +21 -20
  32. pymobiledevice3/cli/version.py +3 -2
  33. pymobiledevice3/cli/webinspector.py +156 -78
  34. pymobiledevice3/common.py +1 -1
  35. pymobiledevice3/exceptions.py +154 -60
  36. pymobiledevice3/irecv.py +49 -53
  37. pymobiledevice3/irecv_devices.py +1489 -492
  38. pymobiledevice3/lockdown.py +400 -251
  39. pymobiledevice3/lockdown_service_provider.py +5 -7
  40. pymobiledevice3/osu/os_utils.py +18 -9
  41. pymobiledevice3/osu/posix_util.py +28 -15
  42. pymobiledevice3/osu/win_util.py +14 -8
  43. pymobiledevice3/pair_records.py +19 -19
  44. pymobiledevice3/remote/common.py +4 -4
  45. pymobiledevice3/remote/core_device/app_service.py +94 -67
  46. pymobiledevice3/remote/core_device/core_device_service.py +17 -14
  47. pymobiledevice3/remote/core_device/device_info.py +5 -5
  48. pymobiledevice3/remote/core_device/diagnostics_service.py +10 -8
  49. pymobiledevice3/remote/core_device/file_service.py +47 -33
  50. pymobiledevice3/remote/remote_service_discovery.py +53 -35
  51. pymobiledevice3/remote/remotexpc.py +64 -42
  52. pymobiledevice3/remote/tunnel_service.py +383 -297
  53. pymobiledevice3/remote/utils.py +14 -13
  54. pymobiledevice3/remote/xpc_message.py +145 -125
  55. pymobiledevice3/resources/dsc_uuid_map.py +19 -19
  56. pymobiledevice3/resources/firmware_notifications.py +16 -16
  57. pymobiledevice3/restore/asr.py +27 -27
  58. pymobiledevice3/restore/base_restore.py +90 -47
  59. pymobiledevice3/restore/consts.py +87 -66
  60. pymobiledevice3/restore/device.py +11 -11
  61. pymobiledevice3/restore/fdr.py +46 -46
  62. pymobiledevice3/restore/ftab.py +19 -19
  63. pymobiledevice3/restore/img4.py +130 -133
  64. pymobiledevice3/restore/mbn.py +587 -0
  65. pymobiledevice3/restore/recovery.py +125 -135
  66. pymobiledevice3/restore/restore.py +535 -523
  67. pymobiledevice3/restore/restore_options.py +122 -115
  68. pymobiledevice3/restore/restored_client.py +25 -22
  69. pymobiledevice3/restore/tss.py +378 -270
  70. pymobiledevice3/service_connection.py +50 -46
  71. pymobiledevice3/services/accessibilityaudit.py +137 -127
  72. pymobiledevice3/services/afc.py +352 -292
  73. pymobiledevice3/services/amfi.py +21 -18
  74. pymobiledevice3/services/companion.py +23 -19
  75. pymobiledevice3/services/crash_reports.py +61 -47
  76. pymobiledevice3/services/debugserver_applist.py +3 -3
  77. pymobiledevice3/services/device_arbitration.py +8 -8
  78. pymobiledevice3/services/device_link.py +56 -48
  79. pymobiledevice3/services/diagnostics.py +971 -968
  80. pymobiledevice3/services/dtfetchsymbols.py +8 -8
  81. pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
  82. pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
  83. pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
  84. pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
  85. pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
  86. pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
  87. pymobiledevice3/services/dvt/instruments/device_info.py +11 -11
  88. pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
  89. pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
  90. pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
  91. pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
  92. pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
  93. pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
  94. pymobiledevice3/services/dvt/instruments/process_control.py +25 -10
  95. pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
  96. pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
  97. pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
  98. pymobiledevice3/services/file_relay.py +10 -10
  99. pymobiledevice3/services/heartbeat.py +8 -7
  100. pymobiledevice3/services/house_arrest.py +12 -15
  101. pymobiledevice3/services/installation_proxy.py +119 -100
  102. pymobiledevice3/services/lockdown_service.py +12 -5
  103. pymobiledevice3/services/misagent.py +22 -19
  104. pymobiledevice3/services/mobile_activation.py +84 -72
  105. pymobiledevice3/services/mobile_config.py +331 -301
  106. pymobiledevice3/services/mobile_image_mounter.py +137 -116
  107. pymobiledevice3/services/mobilebackup2.py +188 -150
  108. pymobiledevice3/services/notification_proxy.py +11 -11
  109. pymobiledevice3/services/os_trace.py +128 -74
  110. pymobiledevice3/services/pcapd.py +306 -306
  111. pymobiledevice3/services/power_assertion.py +10 -9
  112. pymobiledevice3/services/preboard.py +4 -4
  113. pymobiledevice3/services/remote_fetch_symbols.py +16 -14
  114. pymobiledevice3/services/remote_server.py +176 -146
  115. pymobiledevice3/services/restore_service.py +16 -16
  116. pymobiledevice3/services/screenshot.py +13 -10
  117. pymobiledevice3/services/simulate_location.py +7 -7
  118. pymobiledevice3/services/springboard.py +15 -15
  119. pymobiledevice3/services/syslog.py +5 -5
  120. pymobiledevice3/services/web_protocol/alert.py +3 -3
  121. pymobiledevice3/services/web_protocol/automation_session.py +183 -179
  122. pymobiledevice3/services/web_protocol/cdp_screencast.py +44 -36
  123. pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
  124. pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
  125. pymobiledevice3/services/web_protocol/driver.py +47 -45
  126. pymobiledevice3/services/web_protocol/element.py +74 -63
  127. pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
  128. pymobiledevice3/services/web_protocol/selenium_api.py +3 -3
  129. pymobiledevice3/services/web_protocol/session_protocol.py +15 -10
  130. pymobiledevice3/services/web_protocol/switch_to.py +11 -12
  131. pymobiledevice3/services/webinspector.py +142 -116
  132. pymobiledevice3/tcp_forwarder.py +35 -22
  133. pymobiledevice3/tunneld/api.py +20 -15
  134. pymobiledevice3/tunneld/server.py +310 -193
  135. pymobiledevice3/usbmux.py +197 -148
  136. pymobiledevice3/utils.py +14 -11
  137. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/METADATA +1 -2
  138. pymobiledevice3-5.1.2.dist-info/RECORD +173 -0
  139. pymobiledevice3-4.27.4.dist-info/RECORD +0 -172
  140. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/WHEEL +0 -0
  141. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/entry_points.txt +0 -0
  142. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/licenses/LICENSE +0 -0
  143. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/top_level.txt +0 -0
@@ -21,9 +21,8 @@ from socket import create_connection
21
21
  from ssl import VerifyMode
22
22
  from typing import Optional, TextIO, cast
23
23
 
24
- from construct import Const, Container
24
+ from construct import Const, Container, GreedyBytes, GreedyRange, Int8ul, Int16ub, Int64ul, Prefixed, Struct
25
25
  from construct import Enum as ConstructEnum
26
- from construct import GreedyBytes, GreedyRange, Int8ul, Int16ub, Int64ul, Prefixed, Struct
27
26
  from cryptography.hazmat.primitives import hashes
28
27
  from cryptography.hazmat.primitives._serialization import Encoding, NoEncryption, PrivateFormat, PublicFormat
29
28
  from cryptography.hazmat.primitives.asymmetric import rsa
@@ -55,10 +54,20 @@ except ImportError:
55
54
 
56
55
  from pymobiledevice3.bonjour import DEFAULT_BONJOUR_TIMEOUT, browse_remotepairing
57
56
  from pymobiledevice3.ca import make_cert
58
- from pymobiledevice3.exceptions import PairingError, PyMobileDevice3Exception, QuicProtocolNotSupportedError, \
59
- RemotePairingCompletedError, UserDeniedPairingError
60
- from pymobiledevice3.pair_records import PAIRING_RECORD_EXT, create_pairing_records_cache_folder, generate_host_id, \
61
- get_remote_pairing_record_filename, iter_remote_paired_identifiers
57
+ from pymobiledevice3.exceptions import (
58
+ PairingError,
59
+ PyMobileDevice3Exception,
60
+ QuicProtocolNotSupportedError,
61
+ RemotePairingCompletedError,
62
+ UserDeniedPairingError,
63
+ )
64
+ from pymobiledevice3.pair_records import (
65
+ PAIRING_RECORD_EXT,
66
+ create_pairing_records_cache_folder,
67
+ generate_host_id,
68
+ get_remote_pairing_record_filename,
69
+ iter_remote_paired_identifiers,
70
+ )
62
71
  from pymobiledevice3.remote.common import TunnelProtocol
63
72
  from pymobiledevice3.remote.remote_service import RemoteService
64
73
  from pymobiledevice3.remote.remote_service_discovery import RemoteServiceDiscoveryService
@@ -67,7 +76,7 @@ from pymobiledevice3.remote.xpc_message import XpcInt64Type, XpcUInt64Type
67
76
  from pymobiledevice3.service_connection import ServiceConnection
68
77
  from pymobiledevice3.utils import asyncio_print_traceback
69
78
 
70
- DEFAULT_INTERFACE_NAME = 'pymobiledevice3-tunnel'
79
+ DEFAULT_INTERFACE_NAME = "pymobiledevice3-tunnel"
71
80
  TIMEOUT = 1
72
81
 
73
82
  OSUTIL = get_os_utils()
@@ -81,58 +90,60 @@ UDP_HEADER_SIZE = 8
81
90
  IOS_DEVICE_MTU_SIZE = 1500
82
91
  packet_builder.PACKET_MAX_SIZE = IOS_DEVICE_MTU_SIZE - IPV6_HEADER_SIZE - UDP_HEADER_SIZE
83
92
 
84
- PairingDataComponentType = ConstructEnum(Int8ul,
85
- METHOD=0x00,
86
- IDENTIFIER=0x01,
87
- SALT=0x02,
88
- PUBLIC_KEY=0x03,
89
- PROOF=0x04,
90
- ENCRYPTED_DATA=0x05,
91
- STATE=0x06,
92
- ERROR=0x07,
93
- RETRY_DELAY=0x08,
94
- CERTIFICATE=0x09,
95
- SIGNATURE=0x0a,
96
- PERMISSIONS=0x0b,
97
- FRAGMENT_DATA=0x0c,
98
- FRAGMENT_LAST=0x0d,
99
- SESSION_ID=0x0e,
100
- TTL=0x0f,
101
- EXTRA_DATA=0x10,
102
- INFO=0x11,
103
- ACL=0x12,
104
- FLAGS=0x13,
105
- VALIDATION_DATA=0x14,
106
- MFI_AUTH_TOKEN=0x15,
107
- MFI_PRODUCT_TYPE=0x16,
108
- SERIAL_NUMBER=0x17,
109
- MFI_AUTH_TOKEN_UUID=0x18,
110
- APP_FLAGS=0x19,
111
- OWNERSHIP_PROOF=0x1a,
112
- SETUP_CODE_TYPE=0x1b,
113
- PRODUCTION_DATA=0x1c,
114
- APP_INFO=0x1d,
115
- SEPARATOR=0xff)
93
+ PairingDataComponentType = ConstructEnum(
94
+ Int8ul,
95
+ METHOD=0x00,
96
+ IDENTIFIER=0x01,
97
+ SALT=0x02,
98
+ PUBLIC_KEY=0x03,
99
+ PROOF=0x04,
100
+ ENCRYPTED_DATA=0x05,
101
+ STATE=0x06,
102
+ ERROR=0x07,
103
+ RETRY_DELAY=0x08,
104
+ CERTIFICATE=0x09,
105
+ SIGNATURE=0x0A,
106
+ PERMISSIONS=0x0B,
107
+ FRAGMENT_DATA=0x0C,
108
+ FRAGMENT_LAST=0x0D,
109
+ SESSION_ID=0x0E,
110
+ TTL=0x0F,
111
+ EXTRA_DATA=0x10,
112
+ INFO=0x11,
113
+ ACL=0x12,
114
+ FLAGS=0x13,
115
+ VALIDATION_DATA=0x14,
116
+ MFI_AUTH_TOKEN=0x15,
117
+ MFI_PRODUCT_TYPE=0x16,
118
+ SERIAL_NUMBER=0x17,
119
+ MFI_AUTH_TOKEN_UUID=0x18,
120
+ APP_FLAGS=0x19,
121
+ OWNERSHIP_PROOF=0x1A,
122
+ SETUP_CODE_TYPE=0x1B,
123
+ PRODUCTION_DATA=0x1C,
124
+ APP_INFO=0x1D,
125
+ SEPARATOR=0xFF,
126
+ )
116
127
 
117
128
  PairingDataComponentTLV8 = Struct(
118
- 'type' / PairingDataComponentType,
119
- 'data' / Prefixed(Int8ul, GreedyBytes),
129
+ "type" / PairingDataComponentType,
130
+ "data" / Prefixed(Int8ul, GreedyBytes),
120
131
  )
121
132
 
122
133
  PairingDataComponentTLVBuf = GreedyRange(PairingDataComponentTLV8)
123
134
 
124
- PairConsentResult = namedtuple('PairConsentResult', 'public_key salt pin')
135
+ PairConsentResult = namedtuple("PairConsentResult", "public_key salt pin")
125
136
 
126
137
  CDTunnelPacket = Struct(
127
- 'magic' / Const(b'CDTunnel'),
128
- 'body' / Prefixed(Int16ub, GreedyBytes),
138
+ "magic" / Const(b"CDTunnel"),
139
+ "body" / Prefixed(Int16ub, GreedyBytes),
129
140
  )
130
141
 
131
- REPAIRING_PACKET_MAGIC = b'RPPairing'
142
+ REPAIRING_PACKET_MAGIC = b"RPPairing"
132
143
 
133
144
  RPPairingPacket = Struct(
134
- 'magic' / Const(REPAIRING_PACKET_MAGIC),
135
- 'body' / Prefixed(Int16ub, GreedyBytes),
145
+ "magic" / Const(REPAIRING_PACKET_MAGIC),
146
+ "body" / Prefixed(Int16ub, GreedyBytes),
136
147
  )
137
148
 
138
149
 
@@ -140,7 +151,7 @@ class RemotePairingTunnel(ABC):
140
151
  def __init__(self):
141
152
  self._queue = asyncio.Queue()
142
153
  self._tun_read_task = None
143
- self._logger = logging.getLogger(f'{__name__}.{self.__class__.__name__}')
154
+ self._logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
144
155
  self.tun = None
145
156
 
146
157
  @abstractmethod
@@ -159,11 +170,11 @@ class RemotePairingTunnel(ABC):
159
170
  async def tun_read_task(self) -> None:
160
171
  read_size = self.tun.mtu + len(LOOPBACK_HEADER)
161
172
  try:
162
- if sys.platform != 'win32':
173
+ if sys.platform != "win32":
163
174
  while True:
164
175
  packet = await asyncio.to_thread(self.tun.read, read_size)
165
176
  assert packet.startswith(LOOPBACK_HEADER)
166
- packet = packet[len(LOOPBACK_HEADER):]
177
+ packet = packet[len(LOOPBACK_HEADER) :]
167
178
  await self.send_packet_to_device(packet)
168
179
  else:
169
180
  while True:
@@ -174,12 +185,12 @@ class RemotePairingTunnel(ABC):
174
185
  continue
175
186
  await self.send_packet_to_device(packet)
176
187
  except ConnectionResetError:
177
- self._logger.warning(f'got connection reset in {asyncio.current_task().get_name()}')
188
+ self._logger.warning(f"got connection reset in {asyncio.current_task().get_name()}")
178
189
  except OSError:
179
- self._logger.warning(f'got oserror in {asyncio.current_task().get_name()}')
190
+ self._logger.warning(f"got oserror in {asyncio.current_task().get_name()}")
180
191
 
181
192
  def start_tunnel(self, address: str, mtu: int, interface_name=DEFAULT_INTERFACE_NAME) -> None:
182
- if 'win32' == sys.platform:
193
+ if sys.platform == "win32":
183
194
  # Only win32 tunnel implementation supports interface name
184
195
  self.tun = TunTapDevice(interface_name)
185
196
  else:
@@ -187,10 +198,10 @@ class RemotePairingTunnel(ABC):
187
198
  self.tun.addr = address
188
199
  self.tun.mtu = mtu
189
200
  self.tun.up()
190
- self._tun_read_task = asyncio.create_task(self.tun_read_task(), name=f'tun-read-{address}')
201
+ self._tun_read_task = asyncio.create_task(self.tun_read_task(), name=f"tun-read-{address}")
191
202
 
192
203
  async def stop_tunnel(self) -> None:
193
- self._logger.debug('stopping tunnel')
204
+ self._logger.debug(f"[{asyncio.current_task().get_name()}] stopping tunnel")
194
205
  self._tun_read_task.cancel()
195
206
  with suppress(CancelledError):
196
207
  await self._tun_read_task
@@ -200,7 +211,7 @@ class RemotePairingTunnel(ABC):
200
211
 
201
212
  @staticmethod
202
213
  def _encode_cdtunnel_packet(data: dict) -> bytes:
203
- return CDTunnelPacket.build({'body': json.dumps(data).encode()})
214
+ return CDTunnelPacket.build({"body": json.dumps(data).encode()})
204
215
 
205
216
 
206
217
  class RemotePairingQuicTunnel(RemotePairingTunnel, QuicConnectionProtocol):
@@ -214,21 +225,23 @@ class RemotePairingQuicTunnel(RemotePairingTunnel, QuicConnectionProtocol):
214
225
  self._keep_alive_task = None
215
226
 
216
227
  async def wait_closed(self) -> None:
217
- try:
228
+ with suppress(asyncio.CancelledError):
218
229
  await QuicConnectionProtocol.wait_closed(self)
219
- except asyncio.CancelledError:
220
- pass
221
230
 
222
231
  async def send_packet_to_device(self, packet: bytes) -> None:
223
232
  self._quic.send_datagram_frame(packet)
224
233
  self.transmit()
225
234
 
235
+ # Allow other tasks to run
236
+ await asyncio.sleep(0)
237
+
226
238
  async def request_tunnel_establish(self) -> dict:
227
239
  stream_id = self._quic.get_next_available_stream_id()
228
240
  # pad the data with random data to force the MTU size correctly
229
- self._quic.send_datagram_frame(b'x' * 1024)
230
- self._quic.send_stream_data(stream_id, self._encode_cdtunnel_packet(
231
- {'type': 'clientHandshakeRequest', 'mtu': self.REQUESTED_MTU}))
241
+ self._quic.send_datagram_frame(b"x" * 1024)
242
+ self._quic.send_stream_data(
243
+ stream_id, self._encode_cdtunnel_packet({"type": "clientHandshakeRequest", "mtu": self.REQUESTED_MTU})
244
+ )
232
245
  self.transmit()
233
246
  return await self._queue.get()
234
247
 
@@ -258,7 +271,7 @@ class RemotePairingQuicTunnel(RemotePairingTunnel, QuicConnectionProtocol):
258
271
 
259
272
  @staticmethod
260
273
  def _encode_cdtunnel_packet(data: dict) -> bytes:
261
- return CDTunnelPacket.build({'body': json.dumps(data).encode()})
274
+ return CDTunnelPacket.build({"body": json.dumps(data).encode()})
262
275
 
263
276
 
264
277
  class RemotePairingTcpTunnel(RemotePairingTunnel):
@@ -280,30 +293,27 @@ class RemotePairingTcpTunnel(RemotePairingTunnel):
280
293
  while True:
281
294
  try:
282
295
  ipv6_header = await self._reader.readexactly(IPV6_HEADER_SIZE)
283
- ipv6_length = struct.unpack('>H', ipv6_header[4:6])[0]
296
+ ipv6_length = struct.unpack(">H", ipv6_header[4:6])[0]
284
297
  ipv6_body = await self._reader.readexactly(ipv6_length)
285
298
  self.tun.write(LOOPBACK_HEADER + ipv6_header + ipv6_body)
286
299
  except asyncio.exceptions.IncompleteReadError:
287
300
  await asyncio.sleep(1)
288
301
  except OSError as e:
289
- self._logger.warning(f'got {e.__class__.__name__} in {asyncio.current_task().get_name()}')
302
+ self._logger.warning(f"got {e.__class__.__name__} in {asyncio.current_task().get_name()}")
290
303
  await self.wait_closed()
291
304
 
292
305
  async def wait_closed(self) -> None:
293
- try:
306
+ with suppress(OSError):
294
307
  await self._writer.wait_closed()
295
- except OSError:
296
- pass
297
308
 
298
309
  async def request_tunnel_establish(self) -> dict:
299
- self._writer.write(self._encode_cdtunnel_packet(
300
- {'type': 'clientHandshakeRequest', 'mtu': self.REQUESTED_MTU}))
310
+ self._writer.write(self._encode_cdtunnel_packet({"type": "clientHandshakeRequest", "mtu": self.REQUESTED_MTU}))
301
311
  await self._writer.drain()
302
312
  return json.loads(CDTunnelPacket.parse(await self._reader.read(self.REQUESTED_MTU)).body)
303
313
 
304
314
  def start_tunnel(self, address: str, mtu: int, interface_name=DEFAULT_INTERFACE_NAME) -> None:
305
315
  super().start_tunnel(address, mtu, interface_name=interface_name)
306
- self._sock_read_task = asyncio.create_task(self.sock_read_task(), name=f'sock-read-task-{address}')
316
+ self._sock_read_task = asyncio.create_task(self.sock_read_task(), name=f"sock-read-task-{address}")
307
317
 
308
318
  async def stop_tunnel(self) -> None:
309
319
  self._sock_read_task.cancel()
@@ -312,10 +322,8 @@ class RemotePairingTcpTunnel(RemotePairingTunnel):
312
322
  await super().stop_tunnel()
313
323
  if not self._writer.is_closing():
314
324
  self._writer.close()
315
- try:
325
+ with suppress(OSError):
316
326
  await self._writer.wait_closed()
317
- except OSError:
318
- pass
319
327
 
320
328
 
321
329
  @dataclasses.dataclass
@@ -389,112 +397,143 @@ class RemotePairingProtocol(StartTcpTunnel):
389
397
  raise RemotePairingCompletedError()
390
398
 
391
399
  async def create_quic_listener(self, private_key: RSAPrivateKey) -> dict:
392
- request = {'request': {'_0': {'createListener': {
393
- 'key': base64.b64encode(
394
- private_key.public_key().public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
395
- ).decode(),
396
- 'peerConnectionsInfo': [{'owningPID': os.getpid(), 'owningProcessName': 'CoreDeviceService'}],
397
- 'transportProtocolType': 'quic'}}}}
400
+ request = {
401
+ "request": {
402
+ "_0": {
403
+ "createListener": {
404
+ "key": base64.b64encode(
405
+ private_key.public_key().public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
406
+ ).decode(),
407
+ "peerConnectionsInfo": [{"owningPID": os.getpid(), "owningProcessName": "CoreDeviceService"}],
408
+ "transportProtocolType": "quic",
409
+ }
410
+ }
411
+ }
412
+ }
398
413
 
399
414
  response = await self._send_receive_encrypted_request(request)
400
- return response['createListener']
415
+ return response["createListener"]
401
416
 
402
417
  async def create_tcp_listener(self) -> dict:
403
- request = {'request': {'_0': {'createListener': {
404
- 'key': base64.b64encode(self.encryption_key).decode(),
405
- 'peerConnectionsInfo': [{'owningPID': os.getpid(), 'owningProcessName': 'CoreDeviceService'}],
406
- 'transportProtocolType': 'tcp'}}}}
418
+ request = {
419
+ "request": {
420
+ "_0": {
421
+ "createListener": {
422
+ "key": base64.b64encode(self.encryption_key).decode(),
423
+ "peerConnectionsInfo": [{"owningPID": os.getpid(), "owningProcessName": "CoreDeviceService"}],
424
+ "transportProtocolType": "tcp",
425
+ }
426
+ }
427
+ }
428
+ }
407
429
  response = await self._send_receive_encrypted_request(request)
408
- return response['createListener']
430
+ return response["createListener"]
409
431
 
410
432
  @asynccontextmanager
411
433
  async def start_quic_tunnel(
412
- self, secrets_log_file: Optional[TextIO] = None,
413
- max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT) -> AsyncGenerator[TunnelResult, None]:
434
+ self,
435
+ secrets_log_file: Optional[TextIO] = None,
436
+ max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT,
437
+ ) -> AsyncGenerator[TunnelResult, None]:
414
438
  private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
415
439
  parameters = await self.create_quic_listener(private_key)
416
440
  cert = make_cert(private_key, private_key.public_key())
417
441
  configuration = QuicConfiguration(
418
- alpn_protocols=['RemotePairingTunnelProtocol'],
442
+ alpn_protocols=["RemotePairingTunnelProtocol"],
419
443
  is_client=True,
420
444
  verify_mode=VerifyMode.CERT_NONE,
421
445
  verify_hostname=False,
422
446
  max_datagram_frame_size=RemotePairingQuicTunnel.MAX_QUIC_DATAGRAM,
423
- idle_timeout=max_idle_timeout
447
+ idle_timeout=max_idle_timeout,
448
+ )
449
+ configuration.load_cert_chain(
450
+ cert.public_bytes(Encoding.PEM),
451
+ private_key.private_bytes(Encoding.PEM, PrivateFormat.TraditionalOpenSSL, NoEncryption()).decode(),
424
452
  )
425
- configuration.load_cert_chain(cert.public_bytes(Encoding.PEM),
426
- private_key.private_bytes(Encoding.PEM, PrivateFormat.TraditionalOpenSSL,
427
- NoEncryption()).decode())
428
453
  configuration.secrets_log_file = secrets_log_file
429
454
 
430
455
  host = self.hostname
431
- port = parameters['port']
456
+ port = parameters["port"]
432
457
 
433
- self.logger.debug(f'Connecting to {host}:{port}')
458
+ self.logger.debug(f"Connecting to {host}:{port}")
434
459
  try:
435
460
  async with aioquic_connect(
436
- host,
437
- port,
438
- configuration=configuration,
439
- create_protocol=RemotePairingQuicTunnel,
461
+ host,
462
+ port,
463
+ configuration=configuration,
464
+ create_protocol=RemotePairingQuicTunnel,
440
465
  ) as client:
441
- self.logger.debug('quic connected')
466
+ self.logger.debug("quic connected")
442
467
  client = cast(RemotePairingQuicTunnel, client)
443
468
  await client.wait_connected()
444
469
  handshake_response = await client.request_tunnel_establish()
445
- client.start_tunnel(handshake_response['clientParameters']['address'],
446
- handshake_response['clientParameters']['mtu'],
447
- interface_name=f'{DEFAULT_INTERFACE_NAME}-{self.remote_identifier}')
470
+ client.start_tunnel(
471
+ handshake_response["clientParameters"]["address"],
472
+ handshake_response["clientParameters"]["mtu"],
473
+ interface_name=f"{DEFAULT_INTERFACE_NAME}-{self.remote_identifier}",
474
+ )
448
475
  try:
449
476
  yield TunnelResult(
450
- client.tun.name, handshake_response['serverAddress'], handshake_response['serverRSDPort'],
451
- TunnelProtocol.QUIC, client)
477
+ client.tun.name,
478
+ handshake_response["serverAddress"],
479
+ handshake_response["serverRSDPort"],
480
+ TunnelProtocol.QUIC,
481
+ client,
482
+ )
452
483
  finally:
453
484
  await client.stop_tunnel()
454
- except ConnectionError:
485
+ except ConnectionError as e:
455
486
  raise QuicProtocolNotSupportedError(
456
- 'iOS 18.2+ removed QUIC protocol support. Use TCP instead (requires python3.13+)')
487
+ "iOS 18.2+ removed QUIC protocol support. Use TCP instead (requires python3.13+)"
488
+ ) from e
457
489
 
458
490
  @asynccontextmanager
459
491
  async def start_tcp_tunnel(self) -> AsyncGenerator[TunnelResult, None]:
460
492
  parameters = await self.create_tcp_listener()
461
493
  host = self.hostname
462
- port = parameters['port']
494
+ port = parameters["port"]
463
495
  sock = create_connection((host, port))
464
496
  OSUTIL.set_keepalive(sock)
465
497
  if sys.version_info >= (3, 13):
466
498
  ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
467
499
  ctx.check_hostname = False
468
500
  ctx.verify_mode = ssl.CERT_NONE
469
- ctx.set_ciphers('PSK')
501
+ ctx.set_ciphers("PSK")
470
502
  ctx.set_psk_client_callback(lambda hint: (None, self.encryption_key))
471
503
  else:
472
504
  # TODO: remove this when python3.12 becomes deprecated
473
505
  ctx = SSLPSKContext(ssl.PROTOCOL_TLSv1_2)
474
506
  ctx.psk = self.encryption_key
475
- ctx.set_ciphers('PSK')
476
- reader, writer = await asyncio.open_connection(sock=sock, ssl=ctx, server_hostname='')
507
+ ctx.set_ciphers("PSK")
508
+ reader, writer = await asyncio.open_connection(sock=sock, ssl=ctx, server_hostname="")
477
509
  tunnel = RemotePairingTcpTunnel(reader, writer)
478
510
  handshake_response = await tunnel.request_tunnel_establish()
479
511
 
480
- tunnel.start_tunnel(handshake_response['clientParameters']['address'],
481
- handshake_response['clientParameters']['mtu'],
482
- interface_name=f'{DEFAULT_INTERFACE_NAME}-{self.remote_identifier}')
512
+ tunnel.start_tunnel(
513
+ handshake_response["clientParameters"]["address"],
514
+ handshake_response["clientParameters"]["mtu"],
515
+ interface_name=f"{DEFAULT_INTERFACE_NAME}-{self.remote_identifier}",
516
+ )
483
517
 
484
518
  try:
485
519
  yield TunnelResult(
486
- tunnel.tun.name, handshake_response['serverAddress'], handshake_response['serverRSDPort'],
487
- TunnelProtocol.TCP, tunnel)
520
+ tunnel.tun.name,
521
+ handshake_response["serverAddress"],
522
+ handshake_response["serverRSDPort"],
523
+ TunnelProtocol.TCP,
524
+ tunnel,
525
+ )
488
526
  finally:
489
527
  await tunnel.stop_tunnel()
490
528
 
491
529
  def save_pair_record(self) -> None:
492
530
  self.pair_record_path.write_bytes(
493
531
  plistlib.dumps({
494
- 'public_key': self.ed25519_private_key.public_key().public_bytes_raw(),
495
- 'private_key': self.ed25519_private_key.private_bytes_raw(),
496
- 'remote_unlock_host_key': self.remote_unlock_host_key
497
- }))
532
+ "public_key": self.ed25519_private_key.public_key().public_bytes_raw(),
533
+ "private_key": self.ed25519_private_key.private_bytes_raw(),
534
+ "remote_unlock_host_key": self.remote_unlock_host_key,
535
+ })
536
+ )
498
537
  OSUTIL.chown_to_non_sudo_if_needed(self.pair_record_path)
499
538
 
500
539
  @property
@@ -505,17 +544,19 @@ class RemotePairingProtocol(StartTcpTunnel):
505
544
 
506
545
  @property
507
546
  def remote_identifier(self) -> str:
508
- return self.handshake_info['peerDeviceInfo']['identifier']
547
+ return self.handshake_info["peerDeviceInfo"]["identifier"]
509
548
 
510
549
  @property
511
550
  def remote_device_model(self) -> str:
512
- return self.handshake_info['peerDeviceInfo']['model']
551
+ return self.handshake_info["peerDeviceInfo"]["model"]
513
552
 
514
553
  @property
515
554
  def pair_record_path(self) -> Path:
516
555
  pair_records_cache_directory = create_pairing_records_cache_folder()
517
- return (pair_records_cache_directory /
518
- f'{get_remote_pairing_record_filename(self.remote_identifier)}.{PAIRING_RECORD_EXT}')
556
+ return (
557
+ pair_records_cache_directory
558
+ / f"{get_remote_pairing_record_filename(self.remote_identifier)}.{PAIRING_RECORD_EXT}"
559
+ )
519
560
 
520
561
  async def _pair(self) -> None:
521
562
  pairing_consent_result = await self._request_pair_consent()
@@ -527,47 +568,49 @@ class RemotePairingProtocol(StartTcpTunnel):
527
568
  self.save_pair_record()
528
569
 
529
570
  async def _request_pair_consent(self) -> PairConsentResult:
530
- """ Display a Trust / Don't Trust dialog """
571
+ """Display a Trust / Don't Trust dialog"""
531
572
 
532
573
  tlv = PairingDataComponentTLVBuf.build([
533
- {'type': PairingDataComponentType.METHOD, 'data': b'\x00'},
534
- {'type': PairingDataComponentType.STATE, 'data': b'\x01'},
574
+ {"type": PairingDataComponentType.METHOD, "data": b"\x00"},
575
+ {"type": PairingDataComponentType.STATE, "data": b"\x01"},
535
576
  ])
536
577
 
537
- await self._send_pairing_data({'data': tlv,
538
- 'kind': 'setupManualPairing',
539
- 'sendingHost': platform.node(),
540
- 'startNewSession': True})
541
- self.logger.info('Waiting user pairing consent')
578
+ await self._send_pairing_data({
579
+ "data": tlv,
580
+ "kind": "setupManualPairing",
581
+ "sendingHost": platform.node(),
582
+ "startNewSession": True,
583
+ })
584
+ self.logger.info("Waiting user pairing consent")
542
585
  response = await self._receive_plain_response()
543
- response = response['event']['_0']
586
+ response = response["event"]["_0"]
544
587
 
545
588
  pin = None
546
- if 'pairingRejectedWithError' in response:
589
+ if "pairingRejectedWithError" in response:
547
590
  raise PairingError(
548
- response['pairingRejectedWithError']['wrappedError']['userInfo']['NSLocalizedDescription'])
549
- elif 'awaitingUserConsent' in response:
591
+ response["pairingRejectedWithError"]["wrappedError"]["userInfo"]["NSLocalizedDescription"]
592
+ )
593
+ elif "awaitingUserConsent" in response:
550
594
  pairing_data = await self._receive_pairing_data()
551
595
  else:
552
596
  # On tvOS no consent is needed and pairing data is returned immediately.
553
- pairing_data = self._decode_bytes_if_needed(response['pairingData']['_0']['data'])
597
+ pairing_data = self._decode_bytes_if_needed(response["pairingData"]["_0"]["data"])
554
598
  # On tvOS we need pin to setup pairing.
555
- if 'AppleTV' in self.remote_device_model:
556
- pin = input('Enter PIN: ')
599
+ if "AppleTV" in self.remote_device_model:
600
+ pin = input("Enter PIN: ")
557
601
 
558
602
  data = self.decode_tlv(PairingDataComponentTLVBuf.parse(pairing_data))
559
- return PairConsentResult(public_key=data[PairingDataComponentType.PUBLIC_KEY],
560
- salt=data[PairingDataComponentType.SALT],
561
- pin=pin)
603
+ return PairConsentResult(
604
+ public_key=data[PairingDataComponentType.PUBLIC_KEY], salt=data[PairingDataComponentType.SALT], pin=pin
605
+ )
562
606
 
563
607
  def _init_srp_context(self, pairing_consent_result: PairConsentResult) -> None:
564
608
  # Receive server public and salt and process them.
565
- pin = pairing_consent_result.pin or '000000'
609
+ pin = pairing_consent_result.pin or "000000"
566
610
  client_session = SRPClientSession(
567
- SRPContext('Pair-Setup', password=pin, prime=PRIME_3072, generator=PRIME_3072_GEN,
568
- hash_func=hashlib.sha512))
569
- client_session.process(pairing_consent_result.public_key.hex(),
570
- pairing_consent_result.salt.hex())
611
+ SRPContext("Pair-Setup", password=pin, prime=PRIME_3072, generator=PRIME_3072_GEN, hash_func=hashlib.sha512)
612
+ )
613
+ client_session.process(pairing_consent_result.public_key.hex(), pairing_consent_result.salt.hex())
571
614
  self.srp_context = client_session
572
615
  self.encryption_key = binascii.unhexlify(self.srp_context.key)
573
616
 
@@ -576,17 +619,18 @@ class RemotePairingProtocol(StartTcpTunnel):
576
619
  client_session_key_proof = binascii.unhexlify(self.srp_context.key_proof)
577
620
 
578
621
  tlv = PairingDataComponentTLVBuf.build([
579
- {'type': PairingDataComponentType.STATE, 'data': b'\x03'},
580
- {'type': PairingDataComponentType.PUBLIC_KEY, 'data': client_public[:255]},
581
- {'type': PairingDataComponentType.PUBLIC_KEY, 'data': client_public[255:]},
582
- {'type': PairingDataComponentType.PROOF, 'data': client_session_key_proof},
622
+ {"type": PairingDataComponentType.STATE, "data": b"\x03"},
623
+ {"type": PairingDataComponentType.PUBLIC_KEY, "data": client_public[:255]},
624
+ {"type": PairingDataComponentType.PUBLIC_KEY, "data": client_public[255:]},
625
+ {"type": PairingDataComponentType.PROOF, "data": client_session_key_proof},
583
626
  ])
584
627
 
585
628
  response = await self._send_receive_pairing_data({
586
- 'data': tlv,
587
- 'kind': 'setupManualPairing',
588
- 'sendingHost': platform.node(),
589
- 'startNewSession': False})
629
+ "data": tlv,
630
+ "kind": "setupManualPairing",
631
+ "sendingHost": platform.node(),
632
+ "startNewSession": False,
633
+ })
590
634
  data = self.decode_tlv(PairingDataComponentTLVBuf.parse(response))
591
635
  assert self.srp_context.verify_proof(data[PairingDataComponentType.PROOF].hex().encode())
592
636
 
@@ -596,8 +640,8 @@ class RemotePairingProtocol(StartTcpTunnel):
596
640
  setup_encryption_key = HKDF(
597
641
  algorithm=hashes.SHA512(),
598
642
  length=32,
599
- salt=b'Pair-Setup-Encrypt-Salt',
600
- info=b'Pair-Setup-Encrypt-Info',
643
+ salt=b"Pair-Setup-Encrypt-Salt",
644
+ info=b"Pair-Setup-Encrypt-Info",
601
645
  ).derive(self.encryption_key)
602
646
 
603
647
  self.ed25519_private_key = Ed25519PrivateKey.generate()
@@ -607,8 +651,8 @@ class RemotePairingProtocol(StartTcpTunnel):
607
651
  signbuf = HKDF(
608
652
  algorithm=hashes.SHA512(),
609
653
  length=32,
610
- salt=b'Pair-Setup-Controller-Sign-Salt',
611
- info=b'Pair-Setup-Controller-Sign-Info',
654
+ salt=b"Pair-Setup-Controller-Sign-Salt",
655
+ info=b"Pair-Setup-Controller-Sign-Info",
612
656
  ).derive(self.encryption_key)
613
657
 
614
658
  signbuf += self.identifier.encode()
@@ -617,41 +661,45 @@ class RemotePairingProtocol(StartTcpTunnel):
617
661
  self.signature = self.ed25519_private_key.sign(signbuf)
618
662
 
619
663
  device_info = dumps({
620
- 'altIRK': b'\xe9\xe8-\xc0jIykVoT\x00\x19\xb1\xc7{',
621
- 'btAddr': '11:22:33:44:55:66',
622
- 'mac': b'\x11\x22\x33\x44\x55\x66',
623
- 'remotepairing_serial_number': 'AAAAAAAAAAAA',
624
- 'accountID': self.identifier,
625
- 'model': 'computer-model',
626
- 'name': platform.node()
664
+ "altIRK": b"\xe9\xe8-\xc0jIykVoT\x00\x19\xb1\xc7{",
665
+ "btAddr": "11:22:33:44:55:66",
666
+ "mac": b"\x11\x22\x33\x44\x55\x66",
667
+ "remotepairing_serial_number": "AAAAAAAAAAAA",
668
+ "accountID": self.identifier,
669
+ "model": "computer-model",
670
+ "name": platform.node(),
627
671
  })
628
672
 
629
673
  tlv = PairingDataComponentTLVBuf.build([
630
- {'type': PairingDataComponentType.IDENTIFIER, 'data': self.identifier.encode()},
631
- {'type': PairingDataComponentType.PUBLIC_KEY,
632
- 'data': self.ed25519_private_key.public_key().public_bytes_raw()},
633
- {'type': PairingDataComponentType.SIGNATURE, 'data': self.signature},
634
- {'type': PairingDataComponentType.INFO, 'data': device_info},
674
+ {"type": PairingDataComponentType.IDENTIFIER, "data": self.identifier.encode()},
675
+ {
676
+ "type": PairingDataComponentType.PUBLIC_KEY,
677
+ "data": self.ed25519_private_key.public_key().public_bytes_raw(),
678
+ },
679
+ {"type": PairingDataComponentType.SIGNATURE, "data": self.signature},
680
+ {"type": PairingDataComponentType.INFO, "data": device_info},
635
681
  ])
636
682
 
637
683
  cip = ChaCha20Poly1305(setup_encryption_key)
638
- encrypted_data = cip.encrypt(b'\x00\x00\x00\x00PS-Msg05', tlv, b'')
684
+ encrypted_data = cip.encrypt(b"\x00\x00\x00\x00PS-Msg05", tlv, b"")
639
685
 
640
686
  tlv = PairingDataComponentTLVBuf.build([
641
- {'type': PairingDataComponentType.ENCRYPTED_DATA, 'data': encrypted_data[:255]},
642
- {'type': PairingDataComponentType.ENCRYPTED_DATA, 'data': encrypted_data[255:]},
643
- {'type': PairingDataComponentType.STATE, 'data': b'\x05'},
687
+ {"type": PairingDataComponentType.ENCRYPTED_DATA, "data": encrypted_data[:255]},
688
+ {"type": PairingDataComponentType.ENCRYPTED_DATA, "data": encrypted_data[255:]},
689
+ {"type": PairingDataComponentType.STATE, "data": b"\x05"},
644
690
  ])
645
691
 
646
692
  response = await self._send_receive_pairing_data({
647
- 'data': tlv,
648
- 'kind': 'setupManualPairing',
649
- 'sendingHost': platform.node(),
650
- 'startNewSession': False})
693
+ "data": tlv,
694
+ "kind": "setupManualPairing",
695
+ "sendingHost": platform.node(),
696
+ "startNewSession": False,
697
+ })
651
698
  data = self.decode_tlv(PairingDataComponentTLVBuf.parse(response))
652
699
 
653
- tlv = PairingDataComponentTLVBuf.parse(cip.decrypt(
654
- b'\x00\x00\x00\x00PS-Msg06', data[PairingDataComponentType.ENCRYPTED_DATA], b''))
700
+ tlv = PairingDataComponentTLVBuf.parse(
701
+ cip.decrypt(b"\x00\x00\x00\x00PS-Msg06", data[PairingDataComponentType.ENCRYPTED_DATA], b"")
702
+ )
655
703
 
656
704
  return tlv
657
705
 
@@ -660,7 +708,7 @@ class RemotePairingProtocol(StartTcpTunnel):
660
708
  algorithm=hashes.SHA512(),
661
709
  length=32,
662
710
  salt=None,
663
- info=b'ClientEncrypt-main',
711
+ info=b"ClientEncrypt-main",
664
712
  ).derive(self.encryption_key)
665
713
  self.client_cip = ChaCha20Poly1305(client_key)
666
714
 
@@ -668,22 +716,23 @@ class RemotePairingProtocol(StartTcpTunnel):
668
716
  algorithm=hashes.SHA512(),
669
717
  length=32,
670
718
  salt=None,
671
- info=b'ServerEncrypt-main',
719
+ info=b"ServerEncrypt-main",
672
720
  ).derive(self.encryption_key)
673
721
  self.server_cip = ChaCha20Poly1305(server_key)
674
722
 
675
723
  async def _create_remote_unlock(self) -> None:
676
724
  try:
677
- response = await self._send_receive_encrypted_request({'request': {'_0': {'createRemoteUnlockKey': {}}}})
678
- self.remote_unlock_host_key = response['createRemoteUnlockKey']['hostKey']
725
+ response = await self._send_receive_encrypted_request({"request": {"_0": {"createRemoteUnlockKey": {}}}})
726
+ self.remote_unlock_host_key = response["createRemoteUnlockKey"]["hostKey"]
679
727
  except PyMobileDevice3Exception:
680
728
  # tvOS does not support remote unlock.
681
- self.remote_unlock_host_key = ''
729
+ self.remote_unlock_host_key = ""
682
730
 
683
731
  async def _attempt_pair_verify(self) -> None:
684
732
  self.handshake_info = await self._send_receive_handshake({
685
- 'hostOptions': {'attemptPairVerify': True},
686
- 'wireProtocolVersion': XpcInt64Type(self.WIRE_PROTOCOL_VERSION)})
733
+ "hostOptions": {"attemptPairVerify": True},
734
+ "wireProtocolVersion": XpcInt64Type(self.WIRE_PROTOCOL_VERSION),
735
+ })
687
736
 
688
737
  @staticmethod
689
738
  def _decode_bytes_if_needed(data: bytes) -> bytes:
@@ -691,13 +740,17 @@ class RemotePairingProtocol(StartTcpTunnel):
691
740
 
692
741
  async def _validate_pairing(self) -> bool:
693
742
  pairing_data = PairingDataComponentTLVBuf.build([
694
- {'type': PairingDataComponentType.STATE, 'data': b'\x01'},
695
- {'type': PairingDataComponentType.PUBLIC_KEY,
696
- 'data': self.x25519_private_key.public_key().public_bytes_raw()},
743
+ {"type": PairingDataComponentType.STATE, "data": b"\x01"},
744
+ {
745
+ "type": PairingDataComponentType.PUBLIC_KEY,
746
+ "data": self.x25519_private_key.public_key().public_bytes_raw(),
747
+ },
697
748
  ])
698
- response = await self._send_receive_pairing_data({'data': pairing_data,
699
- 'kind': 'verifyManualPairing',
700
- 'startNewSession': True})
749
+ response = await self._send_receive_pairing_data({
750
+ "data": pairing_data,
751
+ "kind": "verifyManualPairing",
752
+ "startNewSession": True,
753
+ })
701
754
  data = self.decode_tlv(PairingDataComponentTLVBuf.parse(response))
702
755
 
703
756
  if PairingDataComponentType.ERROR in data:
@@ -710,8 +763,8 @@ class RemotePairingProtocol(StartTcpTunnel):
710
763
  derived_key = HKDF(
711
764
  algorithm=hashes.SHA512(),
712
765
  length=32,
713
- salt=b'Pair-Verify-Encrypt-Salt',
714
- info=b'Pair-Verify-Encrypt-Info',
766
+ salt=b"Pair-Verify-Encrypt-Salt",
767
+ info=b"Pair-Verify-Encrypt-Info",
715
768
  ).derive(self.encryption_key)
716
769
  cip = ChaCha20Poly1305(derived_key)
717
770
 
@@ -720,31 +773,36 @@ class RemotePairingProtocol(StartTcpTunnel):
720
773
  # do so. instead, we verify using the next stage
721
774
 
722
775
  if self.pair_record is None:
723
- private_key = Ed25519PrivateKey.from_private_bytes(b'\x00' * 0x20)
776
+ private_key = Ed25519PrivateKey.from_private_bytes(b"\x00" * 0x20)
724
777
  else:
725
- private_key = Ed25519PrivateKey.from_private_bytes(self.pair_record['private_key'])
778
+ private_key = Ed25519PrivateKey.from_private_bytes(self.pair_record["private_key"])
726
779
 
727
- signbuf = b''
780
+ signbuf = b""
728
781
  signbuf += self.x25519_private_key.public_key().public_bytes_raw()
729
782
  signbuf += self.identifier.encode()
730
783
  signbuf += peer_public_key.public_bytes_raw()
731
784
 
732
785
  signature = private_key.sign(signbuf)
733
786
 
734
- encrypted_data = cip.encrypt(b'\x00\x00\x00\x00PV-Msg03', PairingDataComponentTLVBuf.build([
735
- {'type': PairingDataComponentType.IDENTIFIER, 'data': self.identifier.encode()},
736
- {'type': PairingDataComponentType.SIGNATURE, 'data': signature},
737
- ]), b'')
787
+ encrypted_data = cip.encrypt(
788
+ b"\x00\x00\x00\x00PV-Msg03",
789
+ PairingDataComponentTLVBuf.build([
790
+ {"type": PairingDataComponentType.IDENTIFIER, "data": self.identifier.encode()},
791
+ {"type": PairingDataComponentType.SIGNATURE, "data": signature},
792
+ ]),
793
+ b"",
794
+ )
738
795
 
739
796
  pairing_data = PairingDataComponentTLVBuf.build([
740
- {'type': PairingDataComponentType.STATE, 'data': b'\x03'},
741
- {'type': PairingDataComponentType.ENCRYPTED_DATA, 'data': encrypted_data},
797
+ {"type": PairingDataComponentType.STATE, "data": b"\x03"},
798
+ {"type": PairingDataComponentType.ENCRYPTED_DATA, "data": encrypted_data},
742
799
  ])
743
800
 
744
801
  response = await self._send_receive_pairing_data({
745
- 'data': pairing_data,
746
- 'kind': 'verifyManualPairing',
747
- 'startNewSession': False})
802
+ "data": pairing_data,
803
+ "kind": "verifyManualPairing",
804
+ "startNewSession": False,
805
+ })
748
806
  data = self.decode_tlv(PairingDataComponentTLVBuf.parse(response))
749
807
 
750
808
  if PairingDataComponentType.ERROR in data:
@@ -754,66 +812,68 @@ class RemotePairingProtocol(StartTcpTunnel):
754
812
  return True
755
813
 
756
814
  async def _send_pair_verify_failed(self) -> None:
757
- await self._send_plain_request({'event': {'_0': {'pairVerifyFailed': {}}}})
815
+ await self._send_plain_request({"event": {"_0": {"pairVerifyFailed": {}}}})
758
816
 
759
817
  async def _send_receive_encrypted_request(self, request: dict) -> dict:
760
- nonce = Int64ul.build(self._encrypted_sequence_number) + b'\x00' * 4
761
- encrypted_data = self.client_cip.encrypt(
762
- nonce,
763
- json.dumps(request).encode(),
764
- b'')
765
-
766
- response = await self.send_receive_request({'message': {
767
- 'streamEncrypted': {'_0': encrypted_data}},
768
- 'originatedBy': 'host',
769
- 'sequenceNumber': XpcUInt64Type(self._sequence_number)})
818
+ nonce = Int64ul.build(self._encrypted_sequence_number) + b"\x00" * 4
819
+ encrypted_data = self.client_cip.encrypt(nonce, json.dumps(request).encode(), b"")
820
+
821
+ response = await self.send_receive_request({
822
+ "message": {"streamEncrypted": {"_0": encrypted_data}},
823
+ "originatedBy": "host",
824
+ "sequenceNumber": XpcUInt64Type(self._sequence_number),
825
+ })
770
826
  self._encrypted_sequence_number += 1
771
827
 
772
- encrypted_data = self._decode_bytes_if_needed(response['message']['streamEncrypted']['_0'])
828
+ encrypted_data = self._decode_bytes_if_needed(response["message"]["streamEncrypted"]["_0"])
773
829
  plaintext = self.server_cip.decrypt(nonce, encrypted_data, None)
774
- response = json.loads(plaintext)['response']['_1']
830
+ response = json.loads(plaintext)["response"]["_1"]
775
831
 
776
- if 'errorExtended' in response:
777
- raise PyMobileDevice3Exception(response['errorExtended']['_0']['userInfo']['NSLocalizedDescription'])
832
+ if "errorExtended" in response:
833
+ raise PyMobileDevice3Exception(response["errorExtended"]["_0"]["userInfo"]["NSLocalizedDescription"])
778
834
 
779
835
  return response
780
836
 
781
837
  async def _send_receive_handshake(self, handshake_data: dict) -> dict:
782
- response = await self._send_receive_plain_request({'request': {'_0': {'handshake': {'_0': handshake_data}}}})
783
- return response['response']['_1']['handshake']['_0']
838
+ response = await self._send_receive_plain_request({"request": {"_0": {"handshake": {"_0": handshake_data}}}})
839
+ return response["response"]["_1"]["handshake"]["_0"]
784
840
 
785
841
  async def _send_receive_pairing_data(self, pairing_data: dict) -> bytes:
786
842
  await self._send_pairing_data(pairing_data)
787
843
  return await self._receive_pairing_data()
788
844
 
789
845
  async def _send_pairing_data(self, pairing_data: dict) -> None:
790
- await self._send_plain_request({'event': {'_0': {'pairingData': {'_0': pairing_data}}}})
846
+ await self._send_plain_request({"event": {"_0": {"pairingData": {"_0": pairing_data}}}})
791
847
 
792
848
  async def _receive_pairing_data(self) -> bytes:
793
849
  response = await self._receive_plain_response()
794
- response = response['event']['_0']
795
- if 'pairingData' in response:
796
- return self._decode_bytes_if_needed(response['pairingData']['_0']['data'])
797
- if 'pairingRejectedWithError' in response:
798
- raise UserDeniedPairingError(response['pairingRejectedWithError']
799
- .get('wrappedError', {})
800
- .get('userInfo', {})
801
- .get('NSLocalizedDescription'))
802
- raise PyMobileDevice3Exception(f'Got an unknown state message: {response}')
850
+ response = response["event"]["_0"]
851
+ if "pairingData" in response:
852
+ return self._decode_bytes_if_needed(response["pairingData"]["_0"]["data"])
853
+ if "pairingRejectedWithError" in response:
854
+ raise UserDeniedPairingError(
855
+ response["pairingRejectedWithError"]
856
+ .get("wrappedError", {})
857
+ .get("userInfo", {})
858
+ .get("NSLocalizedDescription")
859
+ )
860
+ raise PyMobileDevice3Exception(f"Got an unknown state message: {response}")
803
861
 
804
862
  async def _send_receive_plain_request(self, plain_request: dict):
805
863
  await self._send_plain_request(plain_request)
806
864
  return await self._receive_plain_response()
807
865
 
808
866
  async def _send_plain_request(self, plain_request: dict) -> None:
809
- await self.send_request({'message': {'plain': {'_0': plain_request}},
810
- 'originatedBy': 'host',
811
- 'sequenceNumber': XpcUInt64Type(self._sequence_number)})
867
+ await self.send_request({
868
+ "message": {"plain": {"_0": plain_request}},
869
+ "originatedBy": "host",
870
+ "sequenceNumber": XpcUInt64Type(self._sequence_number),
871
+ })
812
872
  self._sequence_number += 1
813
873
 
814
874
  async def _receive_plain_response(self) -> dict:
815
875
  response = await self.receive_response()
816
- return response['message']['plain']['_0']
876
+ return response["message"]["plain"]["_0"]
817
877
 
818
878
  @staticmethod
819
879
  def decode_tlv(tlv_list: list[Container]) -> dict:
@@ -825,7 +885,7 @@ class RemotePairingProtocol(StartTcpTunnel):
825
885
  result[tlv.type] = tlv.data
826
886
  return result
827
887
 
828
- async def __aenter__(self) -> 'CoreDeviceTunnelService':
888
+ async def __aenter__(self) -> "CoreDeviceTunnelService":
829
889
  return self
830
890
 
831
891
  async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
@@ -833,7 +893,7 @@ class RemotePairingProtocol(StartTcpTunnel):
833
893
 
834
894
 
835
895
  class CoreDeviceTunnelService(RemotePairingProtocol, RemoteService):
836
- SERVICE_NAME = 'com.apple.internal.dt.coredevice.untrusted.tunnelservice'
896
+ SERVICE_NAME = "com.apple.internal.dt.coredevice.untrusted.tunnelservice"
837
897
 
838
898
  def __init__(self, rsd: RemoteServiceDiscoveryService):
839
899
  RemoteService.__init__(self, rsd, self.SERVICE_NAME)
@@ -845,12 +905,12 @@ class CoreDeviceTunnelService(RemotePairingProtocol, RemoteService):
845
905
  await RemoteService.connect(self)
846
906
  try:
847
907
  response = await self.service.receive_response()
848
- self.version = response['ServiceVersion']
908
+ self.version = response["ServiceVersion"]
849
909
 
850
910
  # Perform pairing if necessary and start a trusted RemoteXPC connection
851
911
  await RemotePairingProtocol.connect(self, autopair=autopair)
852
912
  self.hostname = self.service.address[0]
853
- except Exception: # noqa: E722
913
+ except Exception:
854
914
  await self.service.close()
855
915
  raise
856
916
 
@@ -860,11 +920,13 @@ class CoreDeviceTunnelService(RemotePairingProtocol, RemoteService):
860
920
 
861
921
  async def receive_response(self) -> dict:
862
922
  response = await self.service.receive_response()
863
- return response['value']
923
+ return response["value"]
864
924
 
865
925
  async def send_request(self, data: dict) -> None:
866
926
  return await self.service.send_request({
867
- 'mangledTypeName': 'RemotePairing.ControlChannelMessageEnvelope', 'value': data})
927
+ "mangledTypeName": "RemotePairing.ControlChannelMessageEnvelope",
928
+ "value": data,
929
+ })
868
930
 
869
931
 
870
932
  class RemotePairingTunnelService(RemotePairingProtocol):
@@ -889,7 +951,7 @@ class RemotePairingTunnelService(RemotePairingProtocol):
889
951
  if not await self._validate_pairing():
890
952
  raise ConnectionAbortedError()
891
953
  self._init_client_server_main_encryption_keys()
892
- except: # noqa: E722
954
+ except Exception:
893
955
  await self.close()
894
956
  raise
895
957
 
@@ -897,21 +959,20 @@ class RemotePairingTunnelService(RemotePairingProtocol):
897
959
  if self._writer is None:
898
960
  return
899
961
  self._writer.close()
900
- try:
962
+ with suppress(ssl.SSLError):
901
963
  await self._writer.wait_closed()
902
- except ssl.SSLError:
903
- pass
904
964
  self._writer = None
905
965
  self._reader = None
906
966
 
907
967
  async def receive_response(self) -> dict:
908
968
  await self._reader.readexactly(len(REPAIRING_PACKET_MAGIC))
909
- size = struct.unpack('>H', await self._reader.readexactly(2))[0]
969
+ size = struct.unpack(">H", await self._reader.readexactly(2))[0]
910
970
  return json.loads(await self._reader.readexactly(size))
911
971
 
912
972
  async def send_request(self, data: dict) -> None:
913
973
  self._writer.write(
914
- RPPairingPacket.build({'body': json.dumps(data, default=self._default_json_encoder).encode()}))
974
+ RPPairingPacket.build({"body": json.dumps(data, default=self._default_json_encoder).encode()})
975
+ )
915
976
  await self._writer.drain()
916
977
 
917
978
  @staticmethod
@@ -925,8 +986,9 @@ class RemotePairingTunnelService(RemotePairingProtocol):
925
986
  return base64.b64decode(data)
926
987
 
927
988
  def __repr__(self) -> str:
928
- return (f'<{self.__class__.__name__} IDENTIFIER:{self.remote_identifier} HOSTNAME:{self.hostname} '
929
- f'PORT:{self.port}>')
989
+ return (
990
+ f"<{self.__class__.__name__} IDENTIFIER:{self.remote_identifier} HOSTNAME:{self.hostname} PORT:{self.port}>"
991
+ )
930
992
 
931
993
 
932
994
  class RemotePairingManualPairingService(RemotePairingTunnelService):
@@ -937,28 +999,38 @@ class RemotePairingManualPairingService(RemotePairingTunnelService):
937
999
 
938
1000
 
939
1001
  class CoreDeviceTunnelProxy(StartTcpTunnel):
940
- SERVICE_NAME = 'com.apple.internal.devicecompute.CoreDeviceProxy'
1002
+ SERVICE_NAME = "com.apple.internal.devicecompute.CoreDeviceProxy"
941
1003
 
942
- def __init__(self, lockdown: LockdownServiceProvider) -> None:
943
- self._lockdown = lockdown
944
- self._service: Optional[ServiceConnection] = None
1004
+ @classmethod
1005
+ async def create(cls, lockdown: LockdownServiceProvider) -> "CoreDeviceTunnelProxy":
1006
+ return cls(await lockdown.aio_start_lockdown_service(cls.SERVICE_NAME), lockdown.udid)
1007
+
1008
+ def __init__(self, service: ServiceConnection, remote_identifier: str) -> None:
1009
+ self._service: ServiceConnection = service
1010
+ self._remote_identifier: str = remote_identifier
945
1011
 
946
1012
  @property
947
1013
  def remote_identifier(self) -> str:
948
- return self._lockdown.udid
1014
+ return self._remote_identifier
949
1015
 
950
1016
  @asynccontextmanager
951
- async def start_tcp_tunnel(self) -> AsyncGenerator['TunnelResult', None]:
952
- self._service = await self._lockdown.aio_start_lockdown_service(self.SERVICE_NAME)
1017
+ async def start_tcp_tunnel(self) -> AsyncGenerator["TunnelResult", None]:
1018
+ assert self._service is not None, "service must be connected first"
953
1019
  tunnel = RemotePairingTcpTunnel(self._service.reader, self._service.writer)
954
1020
  handshake_response = await tunnel.request_tunnel_establish()
955
- tunnel.start_tunnel(handshake_response['clientParameters']['address'],
956
- handshake_response['clientParameters']['mtu'],
957
- interface_name=f'{DEFAULT_INTERFACE_NAME}-{self.remote_identifier}')
1021
+ tunnel.start_tunnel(
1022
+ handshake_response["clientParameters"]["address"],
1023
+ handshake_response["clientParameters"]["mtu"],
1024
+ interface_name=f"{DEFAULT_INTERFACE_NAME}-{self.remote_identifier}",
1025
+ )
958
1026
  try:
959
1027
  yield TunnelResult(
960
- tunnel.tun.name, handshake_response['serverAddress'], handshake_response['serverRSDPort'],
961
- TunnelProtocol.TCP, tunnel)
1028
+ tunnel.tun.name,
1029
+ handshake_response["serverAddress"],
1030
+ handshake_response["serverRSDPort"],
1031
+ TunnelProtocol.TCP,
1032
+ tunnel,
1033
+ )
962
1034
  finally:
963
1035
  await tunnel.stop_tunnel()
964
1036
 
@@ -968,7 +1040,8 @@ class CoreDeviceTunnelProxy(StartTcpTunnel):
968
1040
 
969
1041
 
970
1042
  async def create_core_device_tunnel_service_using_rsd(
971
- rsd: RemoteServiceDiscoveryService, autopair: bool = True) -> CoreDeviceTunnelService:
1043
+ rsd: RemoteServiceDiscoveryService, autopair: bool = True
1044
+ ) -> CoreDeviceTunnelService:
972
1045
  service = CoreDeviceTunnelService(rsd)
973
1046
  try:
974
1047
  await service.connect(autopair=autopair)
@@ -977,21 +1050,23 @@ async def create_core_device_tunnel_service_using_rsd(
977
1050
  await service.close()
978
1051
  service = CoreDeviceTunnelService(rsd)
979
1052
  await service.connect(autopair=autopair)
980
- except Exception: # noqa: E722
1053
+ except Exception:
981
1054
  await service.close()
982
1055
  raise
983
1056
  return service
984
1057
 
985
1058
 
986
1059
  async def create_core_device_tunnel_service_using_remotepairing(
987
- remote_identifier: str, hostname: str, port: int, autopair: bool = True) -> RemotePairingTunnelService:
1060
+ remote_identifier: str, hostname: str, port: int, autopair: bool = True
1061
+ ) -> RemotePairingTunnelService:
988
1062
  service = RemotePairingTunnelService(remote_identifier, hostname, port)
989
1063
  await service.connect(autopair=autopair)
990
1064
  return service
991
1065
 
992
1066
 
993
1067
  async def create_core_device_service_using_remotepairing_manual_pairing(
994
- remote_identifier: str, hostname: str, port: int, autopair: bool = True) -> RemotePairingTunnelService:
1068
+ remote_identifier: str, hostname: str, port: int, autopair: bool = True
1069
+ ) -> RemotePairingTunnelService:
995
1070
  service = RemotePairingManualPairingService(remote_identifier, hostname, port)
996
1071
  await service.connect(autopair=autopair)
997
1072
  return service
@@ -999,14 +1074,16 @@ async def create_core_device_service_using_remotepairing_manual_pairing(
999
1074
 
1000
1075
  @asynccontextmanager
1001
1076
  async def start_tunnel_over_remotepairing(
1002
- remote_pairing: RemotePairingTunnelService, secrets: Optional[TextIO] = None,
1003
- max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT,
1004
- protocol: TunnelProtocol = TunnelProtocol.QUIC) \
1005
- -> AsyncGenerator[TunnelResult, None]:
1077
+ remote_pairing: RemotePairingTunnelService,
1078
+ secrets: Optional[TextIO] = None,
1079
+ max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT,
1080
+ protocol: TunnelProtocol = TunnelProtocol.QUIC,
1081
+ ) -> AsyncGenerator[TunnelResult, None]:
1006
1082
  async with remote_pairing:
1007
1083
  if protocol == TunnelProtocol.QUIC:
1008
1084
  async with remote_pairing.start_quic_tunnel(
1009
- secrets_log_file=secrets, max_idle_timeout=max_idle_timeout) as tunnel_result:
1085
+ secrets_log_file=secrets, max_idle_timeout=max_idle_timeout
1086
+ ) as tunnel_result:
1010
1087
  yield tunnel_result
1011
1088
  elif protocol == TunnelProtocol.TCP:
1012
1089
  async with remote_pairing.start_tcp_tunnel() as tunnel_result:
@@ -1015,15 +1092,17 @@ async def start_tunnel_over_remotepairing(
1015
1092
 
1016
1093
  @asynccontextmanager
1017
1094
  async def start_tunnel_over_core_device(
1018
- service_provider: CoreDeviceTunnelService, secrets: Optional[TextIO] = None,
1019
- max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT,
1020
- protocol: TunnelProtocol = TunnelProtocol.QUIC) \
1021
- -> AsyncGenerator[TunnelResult, None]:
1095
+ service_provider: CoreDeviceTunnelService,
1096
+ secrets: Optional[TextIO] = None,
1097
+ max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT,
1098
+ protocol: TunnelProtocol = TunnelProtocol.QUIC,
1099
+ ) -> AsyncGenerator[TunnelResult, None]:
1022
1100
  stop_remoted_if_required()
1023
1101
  async with service_provider:
1024
1102
  if protocol == TunnelProtocol.QUIC:
1025
1103
  async with service_provider.start_quic_tunnel(
1026
- secrets_log_file=secrets, max_idle_timeout=max_idle_timeout) as tunnel_result:
1104
+ secrets_log_file=secrets, max_idle_timeout=max_idle_timeout
1105
+ ) as tunnel_result:
1027
1106
  resume_remoted_if_required()
1028
1107
  yield tunnel_result
1029
1108
  elif protocol == TunnelProtocol.TCP:
@@ -1034,57 +1113,64 @@ async def start_tunnel_over_core_device(
1034
1113
 
1035
1114
  @asynccontextmanager
1036
1115
  async def start_tunnel(
1037
- protocol_handler: RemotePairingProtocol, secrets: Optional[TextIO] = None,
1038
- max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT,
1039
- protocol: TunnelProtocol = TunnelProtocol.QUIC) -> AsyncGenerator[TunnelResult, None]:
1116
+ protocol_handler: RemotePairingProtocol,
1117
+ secrets: Optional[TextIO] = None,
1118
+ max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT,
1119
+ protocol: TunnelProtocol = TunnelProtocol.QUIC,
1120
+ ) -> AsyncGenerator[TunnelResult, None]:
1040
1121
  if isinstance(protocol_handler, CoreDeviceTunnelService):
1041
1122
  async with start_tunnel_over_core_device(
1042
- protocol_handler, secrets=secrets, max_idle_timeout=max_idle_timeout, protocol=protocol) as service:
1123
+ protocol_handler, secrets=secrets, max_idle_timeout=max_idle_timeout, protocol=protocol
1124
+ ) as service:
1043
1125
  yield service
1044
1126
  elif isinstance(protocol_handler, RemotePairingTunnelService):
1045
1127
  async with start_tunnel_over_remotepairing(
1046
- protocol_handler, secrets=secrets, max_idle_timeout=max_idle_timeout, protocol=protocol) as service:
1128
+ protocol_handler, secrets=secrets, max_idle_timeout=max_idle_timeout, protocol=protocol
1129
+ ) as service:
1047
1130
  yield service
1048
1131
  elif isinstance(protocol_handler, CoreDeviceTunnelProxy):
1049
1132
  if protocol != TunnelProtocol.TCP:
1050
- raise ValueError('CoreDeviceTunnelProxy protocol can only be TCP')
1133
+ raise ValueError("CoreDeviceTunnelProxy protocol can only be TCP")
1051
1134
  async with protocol_handler.start_tcp_tunnel() as service:
1052
1135
  yield service
1053
1136
  else:
1054
- raise Exception(f'Bad value for protocol_handler: {protocol_handler}')
1137
+ raise TypeError(f"Bad value for protocol_handler: {protocol_handler}")
1055
1138
 
1056
1139
 
1057
1140
  async def get_core_device_tunnel_services(
1058
- bonjour_timeout: float = DEFAULT_BONJOUR_TIMEOUT,
1059
- udid: Optional[str] = None) -> list[CoreDeviceTunnelService]:
1141
+ bonjour_timeout: float = DEFAULT_BONJOUR_TIMEOUT, udid: Optional[str] = None
1142
+ ) -> list[CoreDeviceTunnelService]:
1060
1143
  result = []
1061
1144
  for rsd in await get_rsds(bonjour_timeout=bonjour_timeout, udid=udid):
1062
- if udid is None and ((Version(rsd.product_version) < Version('17.0'))
1063
- and not rsd.product_type.startswith('RealityDevice')):
1064
- logger.debug(f'Skipping {rsd.udid}:, iOS {rsd.product_version} < 17.0')
1145
+ if udid is None and (
1146
+ (Version(rsd.product_version) < Version("17.0")) and not rsd.product_type.startswith("RealityDevice")
1147
+ ):
1148
+ logger.debug(f"Skipping {rsd.udid}:, iOS {rsd.product_version} < 17.0")
1065
1149
  await rsd.close()
1066
1150
  continue
1067
1151
  try:
1068
1152
  result.append(await create_core_device_tunnel_service_using_rsd(rsd))
1069
- except Exception as e:
1070
- logger.error(f'Failed to start service: {rsd}: {e}')
1153
+ except Exception:
1154
+ logger.exception(f"Failed to start service: {rsd}")
1071
1155
  await rsd.close()
1072
1156
  raise
1073
1157
  return result
1074
1158
 
1075
1159
 
1076
1160
  async def get_remote_pairing_tunnel_services(
1077
- bonjour_timeout: float = DEFAULT_BONJOUR_TIMEOUT,
1078
- udid: Optional[str] = None) -> list[RemotePairingTunnelService]:
1161
+ bonjour_timeout: float = DEFAULT_BONJOUR_TIMEOUT, udid: Optional[str] = None
1162
+ ) -> list[RemotePairingTunnelService]:
1079
1163
  result = []
1080
1164
  for answer in await browse_remotepairing(timeout=bonjour_timeout):
1081
- for ip in answer.ips:
1165
+ for address in answer.addresses:
1082
1166
  for identifier in iter_remote_paired_identifiers():
1083
1167
  if udid is not None and identifier != udid:
1084
1168
  continue
1085
1169
  conn = None
1086
1170
  try:
1087
- conn = await create_core_device_tunnel_service_using_remotepairing(identifier, ip, answer.port)
1171
+ conn = await create_core_device_tunnel_service_using_remotepairing(
1172
+ identifier, address.full_ip, answer.port
1173
+ )
1088
1174
  result.append(conn)
1089
1175
  break
1090
1176
  except ConnectionAbortedError: