Skip to content

Commit

Permalink
Merge pull request #1307 from doronz88/bugfix/remotexpc-pairing
Browse files Browse the repository at this point in the history
tunnel_service: raise exception upon successful pairing
  • Loading branch information
doronz88 authored Dec 9, 2024
2 parents d4890d1 + 8aa4fc2 commit c00229a
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 11 deletions.
12 changes: 11 additions & 1 deletion pymobiledevice3/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
'LaunchingApplicationError', 'BadCommandError', 'BadDevError', 'ConnectionFailedError', 'CoreDeviceError',
'AccessDeniedError', 'RSDRequiredError', 'SysdiagnoseTimeoutError', 'GetProhibitedError',
'FeatureNotSupportedError', 'OSNotSupportedError', 'DeprecationError', 'NotEnoughDiskSpaceError',
'CloudConfigurationAlreadyPresentError', 'QuicProtocolNotSupportedError',
'CloudConfigurationAlreadyPresentError', 'QuicProtocolNotSupportedError', 'RemotePairingCompletedError',
]

from typing import Optional
Expand Down Expand Up @@ -395,3 +395,13 @@ def __init__(self, os_name, feature):
class QuicProtocolNotSupportedError(PyMobileDevice3Exception):
""" QUIC tunnel support was removed on iOS 18.2+ """
pass


class RemotePairingCompletedError(PyMobileDevice3Exception):
"""
Raised upon pairing completion using the `remotepairingdeviced` service (RemoteXPC).
remotepairingdeviced closes connection after pairing, so client must re-establish it after pairing is
completed.
"""
pass
34 changes: 24 additions & 10 deletions pymobiledevice3/remote/tunnel_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
from pymobiledevice3.bonjour import DEFAULT_BONJOUR_TIMEOUT, browse_remotepairing
from pymobiledevice3.ca import make_cert
from pymobiledevice3.exceptions import PairingError, PyMobileDevice3Exception, QuicProtocolNotSupportedError, \
UserDeniedPairingError
RemotePairingCompletedError, UserDeniedPairingError
from pymobiledevice3.pair_records import PAIRING_RECORD_EXT, create_pairing_records_cache_folder, generate_host_id, \
get_remote_pairing_record_filename, iter_remote_paired_identifiers
from pymobiledevice3.remote.common import TunnelProtocol
Expand Down Expand Up @@ -370,10 +370,17 @@ async def send_receive_request(self, data: dict) -> dict:
async def connect(self, autopair: bool = True) -> None:
await self._attempt_pair_verify()

if not await self._validate_pairing():
if autopair:
await self._pair()
self._init_client_server_main_encryption_keys()
if await self._validate_pairing():
# Pairing record validation succeeded, so we can just initiate the relevant session keys
self._init_client_server_main_encryption_keys()
return

if autopair:
await self._pair()
await self.close()

# Once pairing is completed, the remote endpoint closes the connection, so it must be re-established
raise RemotePairingCompletedError()

async def create_quic_listener(self, private_key: RSAPrivateKey) -> dict:
request = {'request': {'_0': {'createListener': {
Expand Down Expand Up @@ -683,7 +690,6 @@ async def _validate_pairing(self) -> bool:
response = await self._send_receive_pairing_data({'data': pairing_data,
'kind': 'verifyManualPairing',
'startNewSession': True})

data = self.decode_tlv(PairingDataComponentTLVBuf.parse(response))

if PairingDataComponentType.ERROR in data:
Expand Down Expand Up @@ -827,16 +833,18 @@ def __init__(self, rsd: RemoteServiceDiscoveryService):
self.version: Optional[int] = None

async def connect(self, autopair: bool = True) -> None:
# Establish RemoteXPC connection to `SERVICE_NAME`
await RemoteService.connect(self)
try:
response = await self.service.receive_response()
self.version = response['ServiceVersion']

# Perform pairing if necessary and start a trusted RemoteXPC connection
await RemotePairingProtocol.connect(self, autopair=autopair)
self.hostname = self.service.address[0]
except Exception as e: # noqa: E722
except Exception: # noqa: E722
await self.service.close()
if isinstance(e, UserDeniedPairingError):
raise
raise

async def close(self) -> None:
await self.rsd.close()
Expand Down Expand Up @@ -953,7 +961,13 @@ async def close(self) -> None:
async def create_core_device_tunnel_service_using_rsd(
rsd: RemoteServiceDiscoveryService, autopair: bool = True) -> CoreDeviceTunnelService:
service = CoreDeviceTunnelService(rsd)
await service.connect(autopair=autopair)
try:
await service.connect(autopair=autopair)
except RemotePairingCompletedError:
# The connection must be reestablished upon pairing is completed
await service.close()
service = CoreDeviceTunnelService(rsd)
await service.connect(autopair=autopair)
return service


Expand Down

0 comments on commit c00229a

Please sign in to comment.