pymobiledevice3 4.14.6__py3-none-any.whl → 7.0.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- misc/plist_sniffer.py +15 -15
- misc/remotexpc_sniffer.py +29 -28
- misc/understanding_idevice_protocol_layers.md +15 -10
- pymobiledevice3/__main__.py +317 -127
- pymobiledevice3/_version.py +22 -4
- pymobiledevice3/bonjour.py +358 -113
- pymobiledevice3/ca.py +253 -16
- pymobiledevice3/cli/activation.py +31 -23
- pymobiledevice3/cli/afc.py +49 -40
- pymobiledevice3/cli/amfi.py +16 -21
- pymobiledevice3/cli/apps.py +87 -42
- pymobiledevice3/cli/backup.py +160 -90
- pymobiledevice3/cli/bonjour.py +44 -40
- pymobiledevice3/cli/cli_common.py +204 -198
- pymobiledevice3/cli/companion_proxy.py +14 -14
- pymobiledevice3/cli/crash.py +105 -56
- pymobiledevice3/cli/developer/__init__.py +62 -0
- pymobiledevice3/cli/developer/accessibility/__init__.py +65 -0
- pymobiledevice3/cli/developer/accessibility/settings.py +43 -0
- pymobiledevice3/cli/developer/arbitration.py +50 -0
- pymobiledevice3/cli/developer/condition.py +33 -0
- pymobiledevice3/cli/developer/core_device.py +294 -0
- pymobiledevice3/cli/developer/debugserver.py +244 -0
- pymobiledevice3/cli/developer/dvt/__init__.py +438 -0
- pymobiledevice3/cli/developer/dvt/core_profile_session.py +295 -0
- pymobiledevice3/cli/developer/dvt/simulate_location.py +56 -0
- pymobiledevice3/cli/developer/dvt/sysmon/__init__.py +69 -0
- pymobiledevice3/cli/developer/dvt/sysmon/process.py +188 -0
- pymobiledevice3/cli/developer/fetch_symbols.py +108 -0
- pymobiledevice3/cli/developer/simulate_location.py +51 -0
- pymobiledevice3/cli/diagnostics/__init__.py +75 -0
- pymobiledevice3/cli/diagnostics/battery.py +47 -0
- pymobiledevice3/cli/idam.py +42 -0
- pymobiledevice3/cli/lockdown.py +108 -103
- pymobiledevice3/cli/mounter.py +158 -99
- pymobiledevice3/cli/notification.py +38 -26
- pymobiledevice3/cli/pcap.py +45 -24
- pymobiledevice3/cli/power_assertion.py +18 -17
- pymobiledevice3/cli/processes.py +17 -23
- pymobiledevice3/cli/profile.py +165 -109
- pymobiledevice3/cli/provision.py +35 -34
- pymobiledevice3/cli/remote.py +217 -129
- pymobiledevice3/cli/restore.py +159 -143
- pymobiledevice3/cli/springboard.py +63 -53
- pymobiledevice3/cli/syslog.py +193 -86
- pymobiledevice3/cli/usbmux.py +73 -33
- pymobiledevice3/cli/version.py +5 -7
- pymobiledevice3/cli/webinspector.py +376 -214
- pymobiledevice3/common.py +3 -1
- pymobiledevice3/exceptions.py +182 -58
- pymobiledevice3/irecv.py +52 -53
- pymobiledevice3/irecv_devices.py +1489 -464
- pymobiledevice3/lockdown.py +473 -275
- pymobiledevice3/lockdown_service_provider.py +15 -8
- pymobiledevice3/osu/os_utils.py +27 -9
- pymobiledevice3/osu/posix_util.py +34 -15
- pymobiledevice3/osu/win_util.py +14 -8
- pymobiledevice3/pair_records.py +102 -21
- pymobiledevice3/remote/common.py +8 -4
- pymobiledevice3/remote/core_device/app_service.py +94 -67
- pymobiledevice3/remote/core_device/core_device_service.py +17 -14
- pymobiledevice3/remote/core_device/device_info.py +5 -5
- pymobiledevice3/remote/core_device/diagnostics_service.py +19 -4
- pymobiledevice3/remote/core_device/file_service.py +53 -23
- pymobiledevice3/remote/remote_service_discovery.py +79 -45
- pymobiledevice3/remote/remotexpc.py +73 -44
- pymobiledevice3/remote/tunnel_service.py +442 -317
- pymobiledevice3/remote/utils.py +14 -13
- pymobiledevice3/remote/xpc_message.py +145 -125
- pymobiledevice3/resources/dsc_uuid_map.py +19 -19
- pymobiledevice3/resources/firmware_notifications.py +20 -16
- pymobiledevice3/resources/notifications.txt +144 -0
- pymobiledevice3/restore/asr.py +27 -27
- pymobiledevice3/restore/base_restore.py +110 -21
- pymobiledevice3/restore/consts.py +87 -66
- pymobiledevice3/restore/device.py +59 -12
- pymobiledevice3/restore/fdr.py +46 -48
- pymobiledevice3/restore/ftab.py +19 -19
- pymobiledevice3/restore/img4.py +163 -0
- pymobiledevice3/restore/mbn.py +587 -0
- pymobiledevice3/restore/recovery.py +151 -151
- pymobiledevice3/restore/restore.py +562 -544
- pymobiledevice3/restore/restore_options.py +131 -110
- pymobiledevice3/restore/restored_client.py +51 -31
- pymobiledevice3/restore/tss.py +385 -267
- pymobiledevice3/service_connection.py +252 -59
- pymobiledevice3/services/accessibilityaudit.py +202 -120
- pymobiledevice3/services/afc.py +962 -365
- pymobiledevice3/services/amfi.py +24 -30
- pymobiledevice3/services/companion.py +23 -19
- pymobiledevice3/services/crash_reports.py +71 -47
- pymobiledevice3/services/debugserver_applist.py +3 -3
- pymobiledevice3/services/device_arbitration.py +8 -8
- pymobiledevice3/services/device_link.py +101 -79
- pymobiledevice3/services/diagnostics.py +973 -967
- pymobiledevice3/services/dtfetchsymbols.py +8 -8
- pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
- pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
- pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
- pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
- pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
- pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
- pymobiledevice3/services/dvt/instruments/device_info.py +20 -11
- pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
- pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
- pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
- pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
- pymobiledevice3/services/dvt/instruments/process_control.py +35 -10
- pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
- pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
- pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
- pymobiledevice3/services/file_relay.py +10 -10
- pymobiledevice3/services/heartbeat.py +9 -8
- pymobiledevice3/services/house_arrest.py +16 -15
- pymobiledevice3/services/idam.py +20 -0
- pymobiledevice3/services/installation_proxy.py +173 -81
- pymobiledevice3/services/lockdown_service.py +20 -10
- pymobiledevice3/services/misagent.py +22 -19
- pymobiledevice3/services/mobile_activation.py +147 -64
- pymobiledevice3/services/mobile_config.py +331 -294
- pymobiledevice3/services/mobile_image_mounter.py +141 -113
- pymobiledevice3/services/mobilebackup2.py +203 -145
- pymobiledevice3/services/notification_proxy.py +11 -11
- pymobiledevice3/services/os_trace.py +134 -74
- pymobiledevice3/services/pcapd.py +314 -302
- pymobiledevice3/services/power_assertion.py +10 -9
- pymobiledevice3/services/preboard.py +4 -4
- pymobiledevice3/services/remote_fetch_symbols.py +21 -14
- pymobiledevice3/services/remote_server.py +176 -146
- pymobiledevice3/services/restore_service.py +16 -16
- pymobiledevice3/services/screenshot.py +15 -12
- pymobiledevice3/services/simulate_location.py +7 -7
- pymobiledevice3/services/springboard.py +15 -15
- pymobiledevice3/services/syslog.py +5 -5
- pymobiledevice3/services/web_protocol/alert.py +11 -11
- pymobiledevice3/services/web_protocol/automation_session.py +251 -239
- pymobiledevice3/services/web_protocol/cdp_screencast.py +46 -37
- pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
- pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
- pymobiledevice3/services/web_protocol/driver.py +114 -111
- pymobiledevice3/services/web_protocol/element.py +124 -111
- pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
- pymobiledevice3/services/web_protocol/selenium_api.py +49 -49
- pymobiledevice3/services/web_protocol/session_protocol.py +18 -12
- pymobiledevice3/services/web_protocol/switch_to.py +30 -27
- pymobiledevice3/services/webinspector.py +189 -155
- pymobiledevice3/tcp_forwarder.py +87 -69
- pymobiledevice3/tunneld/__init__.py +0 -0
- pymobiledevice3/tunneld/api.py +63 -0
- pymobiledevice3/tunneld/server.py +603 -0
- pymobiledevice3/usbmux.py +198 -147
- pymobiledevice3/utils.py +14 -11
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/METADATA +55 -28
- pymobiledevice3-7.0.6.dist-info/RECORD +188 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/WHEEL +1 -1
- pymobiledevice3/cli/developer.py +0 -1215
- pymobiledevice3/cli/diagnostics.py +0 -99
- pymobiledevice3/tunneld.py +0 -524
- pymobiledevice3-4.14.6.dist-info/RECORD +0 -168
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info/licenses}/LICENSE +0 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/top_level.txt +0 -0
pymobiledevice3/tcp_forwarder.py
CHANGED
|
@@ -2,6 +2,7 @@ import logging
|
|
|
2
2
|
import select
|
|
3
3
|
import socket
|
|
4
4
|
import threading
|
|
5
|
+
import time
|
|
5
6
|
from abc import abstractmethod
|
|
6
7
|
from typing import Optional
|
|
7
8
|
|
|
@@ -18,7 +19,7 @@ class TcpForwarderBase:
|
|
|
18
19
|
MAX_FORWARDED_CONNECTIONS = 200
|
|
19
20
|
TIMEOUT = 1
|
|
20
21
|
|
|
21
|
-
def __init__(self, src_port: int, listening_event: threading.Event = None):
|
|
22
|
+
def __init__(self, src_port: int, listening_event: Optional[threading.Event] = None):
|
|
22
23
|
"""
|
|
23
24
|
Initialize a new tcp forwarder
|
|
24
25
|
|
|
@@ -37,8 +38,8 @@ class TcpForwarderBase:
|
|
|
37
38
|
# socket to its remote socket and vice versa
|
|
38
39
|
self.connections = {}
|
|
39
40
|
|
|
40
|
-
def start(self, address=
|
|
41
|
-
"""
|
|
41
|
+
def start(self, address="0.0.0.0"):
|
|
42
|
+
"""forward each connection from given local machine port to remote device port"""
|
|
42
43
|
# create local tcp server socket
|
|
43
44
|
self.server_socket = socket.socket()
|
|
44
45
|
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
@@ -50,40 +51,41 @@ class TcpForwarderBase:
|
|
|
50
51
|
if self.listening_event:
|
|
51
52
|
self.listening_event.set()
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
self.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
self.
|
|
66
|
-
|
|
67
|
-
if current_sock not in closed_sockets:
|
|
68
|
-
try:
|
|
69
|
-
self._handle_data(current_sock, closed_sockets)
|
|
70
|
-
except ConnectionResetError:
|
|
71
|
-
self.logger.exception("Error when handling data")
|
|
72
|
-
self._handle_close_or_error(current_sock)
|
|
54
|
+
try:
|
|
55
|
+
while self.inputs:
|
|
56
|
+
# will only perform the socket select on the inputs. the outputs will handled
|
|
57
|
+
# as synchronous blocking
|
|
58
|
+
readable, _writable, exceptional = select.select(self.inputs, [], self.inputs, self.TIMEOUT)
|
|
59
|
+
if self.stopped.is_set():
|
|
60
|
+
self.logger.debug("Closing since stopped is set")
|
|
61
|
+
break
|
|
62
|
+
|
|
63
|
+
closed_sockets = set()
|
|
64
|
+
for current_sock in readable:
|
|
65
|
+
self.logger.debug("Processing %r", current_sock)
|
|
66
|
+
if current_sock is self.server_socket:
|
|
67
|
+
self._handle_server_connection()
|
|
73
68
|
else:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
69
|
+
if current_sock not in closed_sockets:
|
|
70
|
+
try:
|
|
71
|
+
self._handle_data(current_sock, closed_sockets)
|
|
72
|
+
except ConnectionResetError:
|
|
73
|
+
self.logger.error("Error when handling data")
|
|
74
|
+
self._handle_close_or_error(current_sock)
|
|
75
|
+
else:
|
|
76
|
+
self.logger.debug("Is closed")
|
|
77
|
+
|
|
78
|
+
for current_sock in exceptional:
|
|
79
|
+
self.logger.error("Sock failed: %r", current_sock)
|
|
80
|
+
self._handle_close_or_error(current_sock)
|
|
81
|
+
finally:
|
|
82
|
+
self.logger.info("Closing everything")
|
|
83
|
+
# on stop, close all currently opened sockets
|
|
84
|
+
for current_sock in self.inputs:
|
|
85
|
+
current_sock.close()
|
|
84
86
|
|
|
85
87
|
def _handle_close_or_error(self, from_sock):
|
|
86
|
-
"""
|
|
88
|
+
"""if an error occurred its time to close the two sockets"""
|
|
87
89
|
other_sock = self.connections[from_sock]
|
|
88
90
|
|
|
89
91
|
other_sock.close()
|
|
@@ -91,56 +93,59 @@ class TcpForwarderBase:
|
|
|
91
93
|
self.inputs.remove(other_sock)
|
|
92
94
|
self.inputs.remove(from_sock)
|
|
93
95
|
|
|
94
|
-
self.logger.info(f
|
|
96
|
+
self.logger.info(f"connection {other_sock} was closed")
|
|
95
97
|
|
|
96
98
|
def _handle_data(self, from_sock, closed_sockets):
|
|
97
|
-
self.logger.debug("Handling data from
|
|
98
|
-
data = None
|
|
99
|
+
self.logger.debug(f"Handling data from {from_sock}")
|
|
99
100
|
try:
|
|
100
101
|
data = from_sock.recv(1024)
|
|
102
|
+
if not data:
|
|
103
|
+
raise ConnectionResetError("Connection closed by the peer.")
|
|
104
|
+
except BlockingIOError:
|
|
105
|
+
self.logger.warning(f"Non-blocking read failed on {from_sock}, retrying later.")
|
|
106
|
+
return
|
|
101
107
|
except OSError:
|
|
102
|
-
|
|
103
|
-
pass
|
|
104
|
-
|
|
105
|
-
if data is None or len(data) == 0:
|
|
106
|
-
if data is None:
|
|
107
|
-
# data is none means we had an error reading from socket
|
|
108
|
-
self.logger.debug("oserror when reading from_sock")
|
|
109
|
-
else:
|
|
110
|
-
# Empty data means socket was closed
|
|
111
|
-
self.logger.info("No data was read from the socket")
|
|
108
|
+
self.logger.error(f"Error reading from socket {from_sock}")
|
|
112
109
|
self._handle_close_or_error(from_sock)
|
|
113
110
|
closed_sockets.add(from_sock)
|
|
114
|
-
closed_sockets.add(self.connections[from_sock])
|
|
115
111
|
return
|
|
116
112
|
|
|
117
|
-
|
|
118
|
-
|
|
113
|
+
other_sock = self.connections.get(from_sock)
|
|
114
|
+
if not other_sock:
|
|
115
|
+
self.logger.error(f"No connection mapping found for {from_sock}.")
|
|
116
|
+
return
|
|
117
|
+
|
|
119
118
|
try:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
total_sent = 0
|
|
120
|
+
while total_sent < len(data):
|
|
121
|
+
try:
|
|
122
|
+
sent = other_sock.send(data[total_sent:])
|
|
123
|
+
total_sent += sent
|
|
124
|
+
except BlockingIOError:
|
|
125
|
+
self.logger.warning(f"Socket buffer full for {other_sock}, retrying in 100ms.")
|
|
126
|
+
time.sleep(0.1) # Introduce a small delay
|
|
127
|
+
except BrokenPipeError:
|
|
128
|
+
self.logger.error(f"Broken pipe error on {other_sock}.")
|
|
129
|
+
raise
|
|
124
130
|
except OSError:
|
|
125
|
-
|
|
126
|
-
self.
|
|
127
|
-
self._handle_close_or_error(other_sock)
|
|
131
|
+
self.logger.error("Unhandled error while forwarding data")
|
|
132
|
+
self._handle_close_or_error(from_sock)
|
|
128
133
|
closed_sockets.add(from_sock)
|
|
129
|
-
closed_sockets.add(
|
|
134
|
+
closed_sockets.add(other_sock)
|
|
130
135
|
|
|
131
136
|
@abstractmethod
|
|
132
137
|
def _establish_remote_connection(self) -> socket.socket:
|
|
133
138
|
pass
|
|
134
139
|
|
|
135
140
|
def _handle_server_connection(self):
|
|
136
|
-
"""
|
|
137
|
-
local_connection,
|
|
141
|
+
"""accept the connection from local machine and attempt to connect at remote"""
|
|
142
|
+
local_connection, _client_address = self.server_socket.accept()
|
|
138
143
|
local_connection.setblocking(False)
|
|
139
144
|
|
|
140
145
|
try:
|
|
141
146
|
remote_connection = self._establish_remote_connection()
|
|
142
147
|
except ConnectionFailedError:
|
|
143
|
-
self.logger.error(f
|
|
148
|
+
self.logger.error(f"failed to connect to port: {self.dst_port}")
|
|
144
149
|
local_connection.close()
|
|
145
150
|
return
|
|
146
151
|
|
|
@@ -154,10 +159,10 @@ class TcpForwarderBase:
|
|
|
154
159
|
self.connections[remote_connection] = local_connection
|
|
155
160
|
self.connections[local_connection] = remote_connection
|
|
156
161
|
|
|
157
|
-
self.logger.info(
|
|
162
|
+
self.logger.info("connection established from local to remote")
|
|
158
163
|
|
|
159
164
|
def stop(self):
|
|
160
|
-
"""
|
|
165
|
+
"""stop forwarding"""
|
|
161
166
|
self.stopped.set()
|
|
162
167
|
|
|
163
168
|
|
|
@@ -166,8 +171,15 @@ class UsbmuxTcpForwarder(TcpForwarderBase):
|
|
|
166
171
|
Allows forwarding local tcp connection into the device via a given lockdown connection
|
|
167
172
|
"""
|
|
168
173
|
|
|
169
|
-
def __init__(
|
|
170
|
-
|
|
174
|
+
def __init__(
|
|
175
|
+
self,
|
|
176
|
+
serial: str,
|
|
177
|
+
dst_port: int,
|
|
178
|
+
src_port: int,
|
|
179
|
+
listening_event: Optional[threading.Event] = None,
|
|
180
|
+
usbmux_connection_type: Optional[str] = None,
|
|
181
|
+
usbmux_address: Optional[str] = None,
|
|
182
|
+
):
|
|
171
183
|
"""
|
|
172
184
|
Initialize a new tcp forwarder
|
|
173
185
|
|
|
@@ -186,8 +198,9 @@ class UsbmuxTcpForwarder(TcpForwarderBase):
|
|
|
186
198
|
|
|
187
199
|
def _establish_remote_connection(self) -> socket.socket:
|
|
188
200
|
# connect directly using usbmuxd
|
|
189
|
-
mux_device = usbmux.select_device(
|
|
190
|
-
|
|
201
|
+
mux_device = usbmux.select_device(
|
|
202
|
+
self.serial, connection_type=self.usbmux_connection_type, usbmux_address=self.usbmux_address
|
|
203
|
+
)
|
|
191
204
|
self.logger.debug("Selected device: %r", mux_device)
|
|
192
205
|
if mux_device is None:
|
|
193
206
|
raise ConnectionFailedError()
|
|
@@ -199,8 +212,13 @@ class LockdownTcpForwarder(TcpForwarderBase):
|
|
|
199
212
|
Allows forwarding local tcp connection into the device via a given lockdown connection
|
|
200
213
|
"""
|
|
201
214
|
|
|
202
|
-
def __init__(
|
|
203
|
-
|
|
215
|
+
def __init__(
|
|
216
|
+
self,
|
|
217
|
+
service_provider: LockdownServiceProvider,
|
|
218
|
+
src_port: int,
|
|
219
|
+
service_name: str,
|
|
220
|
+
listening_event: Optional[threading.Event] = None,
|
|
221
|
+
):
|
|
204
222
|
"""
|
|
205
223
|
Initialize a new tcp forwarder
|
|
206
224
|
|
|
File without changes
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from pymobiledevice3.exceptions import TunneldConnectionError
|
|
6
|
+
from pymobiledevice3.remote.remote_service_discovery import RemoteServiceDiscoveryService
|
|
7
|
+
from pymobiledevice3.utils import get_asyncio_loop
|
|
8
|
+
|
|
9
|
+
TUNNELD_DEFAULT_ADDRESS = ("127.0.0.1", 49151)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def async_get_tunneld_devices(
|
|
13
|
+
tunneld_address: tuple[str, int] = TUNNELD_DEFAULT_ADDRESS,
|
|
14
|
+
) -> list[RemoteServiceDiscoveryService]:
|
|
15
|
+
tunnels = _list_tunnels(tunneld_address)
|
|
16
|
+
return await _create_rsds_from_tunnels(tunnels)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_tunneld_devices(
|
|
20
|
+
tunneld_address: tuple[str, int] = TUNNELD_DEFAULT_ADDRESS,
|
|
21
|
+
) -> list[RemoteServiceDiscoveryService]:
|
|
22
|
+
return get_asyncio_loop().run_until_complete(async_get_tunneld_devices(tunneld_address))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def async_get_tunneld_device_by_udid(
|
|
26
|
+
udid: str, tunneld_address: tuple[str, int] = TUNNELD_DEFAULT_ADDRESS
|
|
27
|
+
) -> Optional[RemoteServiceDiscoveryService]:
|
|
28
|
+
tunnels = _list_tunnels(tunneld_address)
|
|
29
|
+
if udid not in tunnels:
|
|
30
|
+
return None
|
|
31
|
+
rsds = await _create_rsds_from_tunnels({udid: tunnels[udid]})
|
|
32
|
+
return rsds[0]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_tunneld_device_by_udid(
|
|
36
|
+
udid: str, tunneld_address: tuple[str, int] = TUNNELD_DEFAULT_ADDRESS
|
|
37
|
+
) -> Optional[RemoteServiceDiscoveryService]:
|
|
38
|
+
return get_asyncio_loop().run_until_complete(async_get_tunneld_device_by_udid(udid, tunneld_address))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _list_tunnels(tunneld_address: tuple[str, int] = TUNNELD_DEFAULT_ADDRESS) -> dict[str, list[dict]]:
|
|
42
|
+
try:
|
|
43
|
+
# Get the list of tunnels from the specified address
|
|
44
|
+
resp = requests.get(f"http://{tunneld_address[0]}:{tunneld_address[1]}")
|
|
45
|
+
tunnels = resp.json()
|
|
46
|
+
except requests.exceptions.ConnectionError as e:
|
|
47
|
+
raise TunneldConnectionError() from e
|
|
48
|
+
return tunnels
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
async def _create_rsds_from_tunnels(tunnels: dict[str, list[dict]]) -> list[RemoteServiceDiscoveryService]:
|
|
52
|
+
rsds = []
|
|
53
|
+
for _udid, details in tunnels.items():
|
|
54
|
+
for tunnel_details in details:
|
|
55
|
+
rsd = RemoteServiceDiscoveryService(
|
|
56
|
+
(tunnel_details["tunnel-address"], tunnel_details["tunnel-port"]), name=tunnel_details["interface"]
|
|
57
|
+
)
|
|
58
|
+
try:
|
|
59
|
+
await rsd.connect()
|
|
60
|
+
rsds.append(rsd)
|
|
61
|
+
except (TimeoutError, ConnectionError):
|
|
62
|
+
continue
|
|
63
|
+
return rsds
|