debug
This commit is contained in:
BIN
__pycache__/main.cpython-314.pyc
Normal file
BIN
__pycache__/main.cpython-314.pyc
Normal file
Binary file not shown.
BIN
__pycache__/server.cpython-314.pyc
Normal file
BIN
__pycache__/server.cpython-314.pyc
Normal file
Binary file not shown.
404
server.py
404
server.py
@@ -1,9 +1,11 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import dataclasses
|
import dataclasses
|
||||||
|
from http.cookies import BaseCookie
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
|
from sys import float_repr_style
|
||||||
import traceback
|
import traceback
|
||||||
import warnings
|
import warnings
|
||||||
import fastapi
|
import fastapi
|
||||||
@@ -44,13 +46,16 @@ from pymobiledevice3.exceptions import (
|
|||||||
PairingError,
|
PairingError,
|
||||||
QuicProtocolNotSupportedError,
|
QuicProtocolNotSupportedError,
|
||||||
StreamClosedError,
|
StreamClosedError,
|
||||||
TunneldConnectionError
|
TunneldConnectionError,
|
||||||
)
|
)
|
||||||
from pymobiledevice3.lockdown import create_using_usbmux, get_mobdev2_lockdowns
|
from pymobiledevice3.lockdown import create_using_usbmux, get_mobdev2_lockdowns
|
||||||
from pymobiledevice3.osu.os_utils import get_os_utils
|
from pymobiledevice3.osu.os_utils import get_os_utils
|
||||||
from pymobiledevice3.remote.common import TunnelProtocol
|
from pymobiledevice3.remote.common import TunnelProtocol
|
||||||
from pymobiledevice3.remote.module_imports import start_tunnel
|
from pymobiledevice3.remote.module_imports import start_tunnel
|
||||||
from pymobiledevice3.remote.remote_service_discovery import RSD_PORT, RemoteServiceDiscoveryService
|
from pymobiledevice3.remote.remote_service_discovery import (
|
||||||
|
RSD_PORT,
|
||||||
|
RemoteServiceDiscoveryService,
|
||||||
|
)
|
||||||
from pymobiledevice3.remote.tunnel_service import (
|
from pymobiledevice3.remote.tunnel_service import (
|
||||||
CoreDeviceTunnelProxy,
|
CoreDeviceTunnelProxy,
|
||||||
RemotePairingProtocol,
|
RemotePairingProtocol,
|
||||||
@@ -60,7 +65,9 @@ from pymobiledevice3.remote.tunnel_service import (
|
|||||||
)
|
)
|
||||||
from pymobiledevice3.remote.utils import get_rsds, stop_remoted
|
from pymobiledevice3.remote.utils import get_rsds, stop_remoted
|
||||||
from pymobiledevice3.utils import asyncio_print_traceback
|
from pymobiledevice3.utils import asyncio_print_traceback
|
||||||
from pymobiledevice3.services.dvt.instruments.location_simulation import LocationSimulation
|
from pymobiledevice3.services.dvt.instruments.location_simulation import (
|
||||||
|
LocationSimulation,
|
||||||
|
)
|
||||||
from pymobiledevice3.services.dvt.instruments.dvt_provider import DvtProvider
|
from pymobiledevice3.services.dvt.instruments.dvt_provider import DvtProvider
|
||||||
from pymobiledevice3.tunneld.server import TunneldCore, TunnelTask
|
from pymobiledevice3.tunneld.server import TunneldCore, TunnelTask
|
||||||
from pymobiledevice3.tunneld.api import (
|
from pymobiledevice3.tunneld.api import (
|
||||||
@@ -109,15 +116,44 @@ TUNNEL_ACQUIRE_TIMEOUT_SECONDS = 15
|
|||||||
DVT_CONNECT_TIMEOUT_SECONDS = 20
|
DVT_CONNECT_TIMEOUT_SECONDS = 20
|
||||||
|
|
||||||
|
|
||||||
|
class SimulationStatusData:
|
||||||
|
latitude: float
|
||||||
|
longitude: float
|
||||||
|
start: float
|
||||||
|
end: Optional[float]
|
||||||
|
next_move: Optional[float]
|
||||||
|
|
||||||
|
|
||||||
|
class SimulationStatus:
|
||||||
|
status: bool
|
||||||
|
data: Optional[SimulationStatusData]
|
||||||
|
|
||||||
|
|
||||||
|
class SimulationRequestData:
|
||||||
|
latitude: float
|
||||||
|
longitude: float
|
||||||
|
delay: int
|
||||||
|
start: str
|
||||||
|
end: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class SimulationRequest:
|
||||||
|
status: bool
|
||||||
|
data: Optional[SimulationRequestData]
|
||||||
|
|
||||||
|
|
||||||
class LocationSimulationState:
|
class LocationSimulationState:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.latitude: Optional[float] = None
|
self.latitude: Optional[float] = None
|
||||||
self.longitude: Optional[float] = None
|
self.longitude: Optional[float] = None
|
||||||
|
self.next_move: Optional[float] = None
|
||||||
self.udid: Optional[str] = None
|
self.udid: Optional[str] = None
|
||||||
self.simulation_active: bool = False
|
self.simulation_active: bool = False
|
||||||
self.queue: asyncio.Queue = asyncio.Queue()
|
self.queue: asyncio.Queue = asyncio.Queue()
|
||||||
self.simulation_task: Optional[asyncio.Task] = None
|
self.simulation_task: Optional[asyncio.Task] = None
|
||||||
self.sio: socketio.AsyncServer = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*")
|
self.sio: socketio.AsyncServer = socketio.AsyncServer(
|
||||||
|
async_mode="asgi", cors_allowed_origins="*"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TunneldRunnerSio:
|
class TunneldRunnerSio:
|
||||||
@@ -125,15 +161,15 @@ class TunneldRunnerSio:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(
|
def create(
|
||||||
cls,
|
cls,
|
||||||
host: str,
|
host: str,
|
||||||
port: int,
|
port: int,
|
||||||
context: LocationSimulationState = LocationSimulationState(),
|
context: LocationSimulationState = LocationSimulationState(),
|
||||||
protocol: TunnelProtocol = TunnelProtocol.QUIC,
|
protocol: TunnelProtocol = TunnelProtocol.QUIC,
|
||||||
usb_monitor: bool = True,
|
usb_monitor: bool = True,
|
||||||
wifi_monitor: bool = True,
|
wifi_monitor: bool = True,
|
||||||
usbmux_monitor: bool = True,
|
usbmux_monitor: bool = True,
|
||||||
mobdev2_monitor: bool = True,
|
mobdev2_monitor: bool = True,
|
||||||
) -> None:
|
) -> None:
|
||||||
cls(
|
cls(
|
||||||
host,
|
host,
|
||||||
@@ -147,15 +183,15 @@ class TunneldRunnerSio:
|
|||||||
)._run_app()
|
)._run_app()
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
host: str,
|
host: str,
|
||||||
port: int,
|
port: int,
|
||||||
context: LocationSimulationState = LocationSimulationState(),
|
context: LocationSimulationState = LocationSimulationState(),
|
||||||
protocol: TunnelProtocol = TunnelProtocol.QUIC,
|
protocol: TunnelProtocol = TunnelProtocol.QUIC,
|
||||||
usb_monitor: bool = True,
|
usb_monitor: bool = True,
|
||||||
wifi_monitor: bool = True,
|
wifi_monitor: bool = True,
|
||||||
usbmux_monitor: bool = True,
|
usbmux_monitor: bool = True,
|
||||||
mobdev2_monitor: bool = True,
|
mobdev2_monitor: bool = True,
|
||||||
):
|
):
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI):
|
async def lifespan(app: FastAPI):
|
||||||
@@ -170,8 +206,15 @@ class TunneldRunnerSio:
|
|||||||
self.port = port
|
self.port = port
|
||||||
self.protocol = protocol
|
self.protocol = protocol
|
||||||
self.context = context
|
self.context = context
|
||||||
self._tunneld_api_address = ("127.0.0.1" if host in ("0.0.0.0", "::") else host, port)
|
self._tunneld_api_address = (
|
||||||
self._app = FastAPI(title="iOS Device Management API", lifespan=lifespan, cors_allowed_origins="*")
|
"127.0.0.1" if host in ("0.0.0.0", "::") else host,
|
||||||
|
port,
|
||||||
|
)
|
||||||
|
self._app = FastAPI(
|
||||||
|
title="iOS Device Management API",
|
||||||
|
lifespan=lifespan,
|
||||||
|
cors_allowed_origins="*",
|
||||||
|
)
|
||||||
self._asgi_app = socketio.ASGIApp(self.context.sio, self._app)
|
self._asgi_app = socketio.ASGIApp(self.context.sio, self._app)
|
||||||
self._tunneld_core = TunneldCore(
|
self._tunneld_core = TunneldCore(
|
||||||
protocol=protocol,
|
protocol=protocol,
|
||||||
@@ -182,26 +225,39 @@ class TunneldRunnerSio:
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def get_tun(
|
async def get_tun(
|
||||||
udid: Optional[str] = None, max_retries: int = 10, retry_delay: float = 0.5
|
udid: Optional[str] = None, max_retries: int = 10, retry_delay: float = 0.5
|
||||||
) -> RemoteServiceDiscoveryService:
|
) -> RemoteServiceDiscoveryService:
|
||||||
"""Try to connect to tunneld with retries to handle startup delay."""
|
"""Try to connect to tunneld with retries to handle startup delay."""
|
||||||
for attempt in range(max_retries):
|
for attempt in range(max_retries):
|
||||||
try:
|
try:
|
||||||
if udid:
|
if udid:
|
||||||
rsd = await get_tunneld_device_by_udid(udid, self._tunneld_api_address)
|
rsd = await get_tunneld_device_by_udid(
|
||||||
|
udid, self._tunneld_api_address
|
||||||
|
)
|
||||||
if rsd is not None:
|
if rsd is not None:
|
||||||
logger.info("Connected to tunnel for udid %s after %s retries", udid, attempt)
|
logger.info(
|
||||||
|
"Connected to tunnel for udid %s after %s retries",
|
||||||
|
udid,
|
||||||
|
attempt,
|
||||||
|
)
|
||||||
return rsd
|
return rsd
|
||||||
|
|
||||||
rsds = await get_tunneld_devices(self._tunneld_api_address)
|
rsds = await get_tunneld_devices(self._tunneld_api_address)
|
||||||
if rsds:
|
if rsds:
|
||||||
if udid:
|
if udid:
|
||||||
logger.warning("Tunnel for udid %s not found; using first available tunnel", udid)
|
logger.warning(
|
||||||
|
"Tunnel for udid %s not found; using first available tunnel",
|
||||||
|
udid,
|
||||||
|
)
|
||||||
logger.info("Connected to tunneld after %s retries", attempt)
|
logger.info("Connected to tunneld after %s retries", attempt)
|
||||||
return rsds[0]
|
return rsds[0]
|
||||||
except TunneldConnectionError:
|
except TunneldConnectionError:
|
||||||
if attempt < max_retries - 1:
|
if attempt < max_retries - 1:
|
||||||
logger.info("Waiting for tunneld to be ready... (attempt %s/%s)", attempt + 1, max_retries)
|
logger.info(
|
||||||
|
"Waiting for tunneld to be ready... (attempt %s/%s)",
|
||||||
|
attempt + 1,
|
||||||
|
max_retries,
|
||||||
|
)
|
||||||
await asyncio.sleep(retry_delay)
|
await asyncio.sleep(retry_delay)
|
||||||
else:
|
else:
|
||||||
logger.error("Failed to connect to tunneld after max retries")
|
logger.error("Failed to connect to tunneld after max retries")
|
||||||
@@ -221,7 +277,10 @@ class TunneldRunnerSio:
|
|||||||
break
|
break
|
||||||
tun = await get_tun(self.context.udid)
|
tun = await get_tun(self.context.udid)
|
||||||
if tun is not None:
|
if tun is not None:
|
||||||
async with DvtProvider(tun) as dvt, LocationSimulationQueue(dvt, self.context) as locate_simulation:
|
async with (
|
||||||
|
DvtProvider(tun) as dvt,
|
||||||
|
LocationSimulationQueue(dvt, self.context) as locate_simulation,
|
||||||
|
):
|
||||||
await locate_simulation.clear()
|
await locate_simulation.clear()
|
||||||
self.context.simulation_active = False
|
self.context.simulation_active = False
|
||||||
|
|
||||||
@@ -231,21 +290,36 @@ class TunneldRunnerSio:
|
|||||||
try:
|
try:
|
||||||
if self.context.udid is None:
|
if self.context.udid is None:
|
||||||
active_udids = sorted(
|
active_udids = sorted(
|
||||||
{t.udid for t in self._tunneld_core.tunnel_tasks.values() if t.udid is not None and t.tunnel is not None}
|
{
|
||||||
|
t.udid
|
||||||
|
for t in self._tunneld_core.tunnel_tasks.values()
|
||||||
|
if t.udid is not None and t.tunnel is not None
|
||||||
|
}
|
||||||
)
|
)
|
||||||
if len(active_udids) == 1:
|
if len(active_udids) == 1:
|
||||||
self.context.udid = active_udids[0]
|
self.context.udid = active_udids[0]
|
||||||
logger.info("Simulation worker: auto-selected udid=%s from active tunnel", self.context.udid)
|
logger.info(
|
||||||
|
"Simulation worker: auto-selected udid=%s from active tunnel",
|
||||||
|
self.context.udid,
|
||||||
|
)
|
||||||
elif len(active_udids) == 0:
|
elif len(active_udids) == 0:
|
||||||
logger.error("Simulation worker: no active tunnel with udid available")
|
logger.error(
|
||||||
|
"Simulation worker: no active tunnel with udid available"
|
||||||
|
)
|
||||||
await self.context.sio.emit(
|
await self.context.sio.emit(
|
||||||
"error",
|
"error",
|
||||||
{"type": "simulation_no_tunnel", "message": "No active tunnel found. Start tunnel first."},
|
{
|
||||||
|
"type": "simulation_no_tunnel",
|
||||||
|
"message": "No active tunnel found. Start tunnel first.",
|
||||||
|
},
|
||||||
namespace="/",
|
namespace="/",
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
logger.error("Simulation worker: multiple active tunnels; explicit udid required: %s", active_udids)
|
logger.error(
|
||||||
|
"Simulation worker: multiple active tunnels; explicit udid required: %s",
|
||||||
|
active_udids,
|
||||||
|
)
|
||||||
await self.context.sio.emit(
|
await self.context.sio.emit(
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
@@ -257,17 +331,27 @@ class TunneldRunnerSio:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.info("Simulation worker: acquiring tunnel (udid=%s)", self.context.udid)
|
logger.info(
|
||||||
|
"Simulation worker: acquiring tunnel (udid=%s)", self.context.udid
|
||||||
|
)
|
||||||
tun = await asyncio.wait_for(
|
tun = await asyncio.wait_for(
|
||||||
get_tun(self.context.udid),
|
get_tun(self.context.udid),
|
||||||
timeout=TUNNEL_ACQUIRE_TIMEOUT_SECONDS,
|
timeout=TUNNEL_ACQUIRE_TIMEOUT_SECONDS,
|
||||||
)
|
)
|
||||||
logger.info("Simulation worker: tunnel acquired, connecting DVT provider")
|
logger.info(
|
||||||
|
"Simulation worker: tunnel acquired, connecting DVT provider"
|
||||||
|
)
|
||||||
dvt_provider = DvtProvider(tun)
|
dvt_provider = DvtProvider(tun)
|
||||||
dvt = await asyncio.wait_for(dvt_provider.__aenter__(), timeout=DVT_CONNECT_TIMEOUT_SECONDS)
|
dvt = await asyncio.wait_for(
|
||||||
logger.info("Simulation worker: DVT provider connected, starting queue playback")
|
dvt_provider.__aenter__(), timeout=DVT_CONNECT_TIMEOUT_SECONDS
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"Simulation worker: DVT provider connected, starting queue playback"
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
async with LocationSimulationQueue(dvt, self.context) as locate_simulation:
|
async with LocationSimulationQueue(
|
||||||
|
dvt, self.context
|
||||||
|
) as locate_simulation:
|
||||||
await locate_simulation.play_queue()
|
await locate_simulation.play_queue()
|
||||||
finally:
|
finally:
|
||||||
logger.info("Simulation worker: closing DVT provider")
|
logger.info("Simulation worker: closing DVT provider")
|
||||||
@@ -306,10 +390,16 @@ class TunneldRunnerSio:
|
|||||||
for key, value in d.items():
|
for key, value in d.items():
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
iterate_multidim(value)
|
iterate_multidim(value)
|
||||||
elif isinstance(value, str) or isinstance(value, int) or isinstance(value, float) or isinstance(value, bool) or isinstance(value, list):
|
elif (
|
||||||
|
isinstance(value, str)
|
||||||
|
or isinstance(value, int)
|
||||||
|
or isinstance(value, float)
|
||||||
|
or isinstance(value, bool)
|
||||||
|
or isinstance(value, list)
|
||||||
|
):
|
||||||
mydict[key] = value
|
mydict[key] = value
|
||||||
else:
|
else:
|
||||||
mydict[key] = ''
|
mydict[key] = ""
|
||||||
return mydict
|
return mydict
|
||||||
|
|
||||||
@self._app.get("/")
|
@self._app.get("/")
|
||||||
@@ -322,11 +412,13 @@ class TunneldRunnerSio:
|
|||||||
continue
|
continue
|
||||||
if active_tunnel.udid not in tunnels:
|
if active_tunnel.udid not in tunnels:
|
||||||
tunnels[active_tunnel.udid] = []
|
tunnels[active_tunnel.udid] = []
|
||||||
tunnels[active_tunnel.udid].append({
|
tunnels[active_tunnel.udid].append(
|
||||||
"tunnel-address": active_tunnel.tunnel.address,
|
{
|
||||||
"tunnel-port": active_tunnel.tunnel.port,
|
"tunnel-address": active_tunnel.tunnel.address,
|
||||||
"interface": ip,
|
"tunnel-port": active_tunnel.tunnel.port,
|
||||||
})
|
"interface": ip,
|
||||||
|
}
|
||||||
|
)
|
||||||
return tunnels
|
return tunnels
|
||||||
|
|
||||||
@self._app.get("/device_info")
|
@self._app.get("/device_info")
|
||||||
@@ -339,10 +431,14 @@ class TunneldRunnerSio:
|
|||||||
if active_tunnel.udid not in tunnels:
|
if active_tunnel.udid not in tunnels:
|
||||||
tunnels[active_tunnel.udid] = {}
|
tunnels[active_tunnel.udid] = {}
|
||||||
try:
|
try:
|
||||||
lockdown = await create_using_usbmux(serial=active_tunnel.udid, autopair=False)
|
lockdown = await create_using_usbmux(
|
||||||
|
serial=active_tunnel.udid, autopair=False
|
||||||
|
)
|
||||||
tunnels[active_tunnel.udid] = iterate_multidim(lockdown.all_values)
|
tunnels[active_tunnel.udid] = iterate_multidim(lockdown.all_values)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to create lockdown session for device {active_tunnel.udid}: {e}")
|
logger.error(
|
||||||
|
f"Failed to create lockdown session for device {active_tunnel.udid}: {e}"
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
return tunnels
|
return tunnels
|
||||||
|
|
||||||
@@ -350,19 +446,32 @@ class TunneldRunnerSio:
|
|||||||
async def shutdown() -> fastapi.Response:
|
async def shutdown() -> fastapi.Response:
|
||||||
"""Shutdown Tunneld"""
|
"""Shutdown Tunneld"""
|
||||||
os.kill(os.getpid(), signal.SIGINT)
|
os.kill(os.getpid(), signal.SIGINT)
|
||||||
data = {"operation": "shutdown", "data": True, "message": "Server shutting down..."}
|
data = {
|
||||||
|
"operation": "shutdown",
|
||||||
|
"data": True,
|
||||||
|
"message": "Server shutting down...",
|
||||||
|
}
|
||||||
return generate_http_response(data)
|
return generate_http_response(data)
|
||||||
|
|
||||||
@self._app.get("/clear_tunnels")
|
@self._app.get("/clear_tunnels")
|
||||||
async def clear_tunnels() -> fastapi.Response:
|
async def clear_tunnels() -> fastapi.Response:
|
||||||
self._tunneld_core.clear()
|
self._tunneld_core.clear()
|
||||||
data = {"operation": "clear_tunnels", "data": True, "message": "Cleared tunnels..."}
|
data = {
|
||||||
|
"operation": "clear_tunnels",
|
||||||
|
"data": True,
|
||||||
|
"message": "Cleared tunnels...",
|
||||||
|
}
|
||||||
return generate_http_response(data)
|
return generate_http_response(data)
|
||||||
|
|
||||||
@self._app.get("/cancel")
|
@self._app.get("/cancel")
|
||||||
async def cancel_tunnel(udid: str) -> fastapi.Response:
|
async def cancel_tunnel(udid: str) -> fastapi.Response:
|
||||||
self._tunneld_core.cancel(udid=udid)
|
self._tunneld_core.cancel(udid=udid)
|
||||||
data = {"operation": "cancel", "udid": udid, "data": True, "message": f"tunnel {udid} Canceled ..."}
|
data = {
|
||||||
|
"operation": "cancel",
|
||||||
|
"udid": udid,
|
||||||
|
"data": True,
|
||||||
|
"message": f"tunnel {udid} Canceled ...",
|
||||||
|
}
|
||||||
return generate_http_response(data)
|
return generate_http_response(data)
|
||||||
|
|
||||||
@self._app.get("/hello")
|
@self._app.get("/hello")
|
||||||
@@ -371,17 +480,21 @@ class TunneldRunnerSio:
|
|||||||
return generate_http_response(data)
|
return generate_http_response(data)
|
||||||
|
|
||||||
def generate_http_response(
|
def generate_http_response(
|
||||||
data: dict, status_code: int = 200, media_type: str = "application/json"
|
data: dict, status_code: int = 200, media_type: str = "application/json"
|
||||||
) -> fastapi.Response:
|
) -> fastapi.Response:
|
||||||
return fastapi.Response(status_code=status_code, media_type=media_type, content=json.dumps(data))
|
return fastapi.Response(
|
||||||
|
status_code=status_code, media_type=media_type, content=json.dumps(data)
|
||||||
|
)
|
||||||
|
|
||||||
@self._app.get("/start-tunnel")
|
@self._app.get("/start-tunnel")
|
||||||
@self.context.sio.event
|
@self.context.sio.event
|
||||||
async def start_tunnel(
|
async def start_tunnel(
|
||||||
udid: str, ip: Optional[str] = None, connection_type: Optional[str] = None
|
udid: str, ip: Optional[str] = None, connection_type: Optional[str] = None
|
||||||
) -> fastapi.Response:
|
) -> fastapi.Response:
|
||||||
udid_tunnels = [
|
udid_tunnels = [
|
||||||
t.tunnel for t in self._tunneld_core.tunnel_tasks.values() if t.udid == udid and t.tunnel is not None
|
t.tunnel
|
||||||
|
for t in self._tunneld_core.tunnel_tasks.values()
|
||||||
|
if t.udid == udid and t.tunnel is not None
|
||||||
]
|
]
|
||||||
if len(udid_tunnels) > 0:
|
if len(udid_tunnels) > 0:
|
||||||
self.context.udid = udid
|
self.context.udid = udid
|
||||||
@@ -403,13 +516,18 @@ class TunneldRunnerSio:
|
|||||||
service = await CoreDeviceTunnelProxy.create(lockdown)
|
service = await CoreDeviceTunnelProxy.create(lockdown)
|
||||||
task = asyncio.create_task(
|
task = asyncio.create_task(
|
||||||
self._tunneld_core.start_tunnel_task(
|
self._tunneld_core.start_tunnel_task(
|
||||||
task_identifier, service, protocol=TunnelProtocol.TCP, queue=queue
|
task_identifier,
|
||||||
|
service,
|
||||||
|
protocol=TunnelProtocol.TCP,
|
||||||
|
queue=queue,
|
||||||
),
|
),
|
||||||
name=f"start-tunnel-task-{task_identifier}",
|
name=f"start-tunnel-task-{task_identifier}",
|
||||||
)
|
)
|
||||||
self._tunneld_core.tunnel_tasks[task_identifier] = TunnelTask(task=task, udid=udid)
|
self._tunneld_core.tunnel_tasks[task_identifier] = TunnelTask(
|
||||||
|
task=task, udid=udid
|
||||||
|
)
|
||||||
created_task = True
|
created_task = True
|
||||||
except (ConnectionFailedError, InvalidServiceError, MuxException):
|
except ConnectionFailedError, InvalidServiceError, MuxException:
|
||||||
pass
|
pass
|
||||||
if connection_type in ("usb", None):
|
if connection_type in ("usb", None):
|
||||||
for rsd in await get_rsds(udid=udid):
|
for rsd in await get_rsds(udid=udid):
|
||||||
@@ -419,20 +537,28 @@ class TunneldRunnerSio:
|
|||||||
continue
|
continue
|
||||||
task = asyncio.create_task(
|
task = asyncio.create_task(
|
||||||
self._tunneld_core.start_tunnel_task(
|
self._tunneld_core.start_tunnel_task(
|
||||||
rsd_ip, await create_core_device_tunnel_service_using_rsd(rsd), queue=queue
|
rsd_ip,
|
||||||
|
await create_core_device_tunnel_service_using_rsd(rsd),
|
||||||
|
queue=queue,
|
||||||
),
|
),
|
||||||
name=f"start-tunnel-usb-{rsd_ip}",
|
name=f"start-tunnel-usb-{rsd_ip}",
|
||||||
)
|
)
|
||||||
self._tunneld_core.tunnel_tasks[rsd_ip] = TunnelTask(task=task, udid=rsd.udid)
|
self._tunneld_core.tunnel_tasks[rsd_ip] = TunnelTask(
|
||||||
|
task=task, udid=rsd.udid
|
||||||
|
)
|
||||||
created_task = True
|
created_task = True
|
||||||
if not created_task and connection_type in ("wifi", None):
|
if not created_task and connection_type in ("wifi", None):
|
||||||
for remotepairing in await get_remote_pairing_tunnel_services(udid=udid):
|
for remotepairing in await get_remote_pairing_tunnel_services(
|
||||||
|
udid=udid
|
||||||
|
):
|
||||||
remotepairing_ip = remotepairing.hostname
|
remotepairing_ip = remotepairing.hostname
|
||||||
if ip is not None and remotepairing_ip != ip:
|
if ip is not None and remotepairing_ip != ip:
|
||||||
await remotepairing.close()
|
await remotepairing.close()
|
||||||
continue
|
continue
|
||||||
task = asyncio.create_task(
|
task = asyncio.create_task(
|
||||||
self._tunneld_core.start_tunnel_task(remotepairing_ip, remotepairing, queue=queue),
|
self._tunneld_core.start_tunnel_task(
|
||||||
|
remotepairing_ip, remotepairing, queue=queue
|
||||||
|
),
|
||||||
name=f"start-tunnel-wifi-{remotepairing_ip}",
|
name=f"start-tunnel-wifi-{remotepairing_ip}",
|
||||||
)
|
)
|
||||||
self._tunneld_core.tunnel_tasks[remotepairing_ip] = TunnelTask(
|
self._tunneld_core.tunnel_tasks[remotepairing_ip] = TunnelTask(
|
||||||
@@ -442,25 +568,36 @@ class TunneldRunnerSio:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return fastapi.Response(
|
return fastapi.Response(
|
||||||
status_code=501,
|
status_code=501,
|
||||||
content=json.dumps({
|
content=json.dumps(
|
||||||
"error": {
|
{
|
||||||
"exception": e.__class__.__name__,
|
"error": {
|
||||||
"traceback": traceback.format_exc(),
|
"exception": e.__class__.__name__,
|
||||||
|
"traceback": traceback.format_exc(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if not created_task:
|
if not created_task:
|
||||||
return fastapi.Response(status_code=501, content=json.dumps({"error": "task not created"}))
|
return fastapi.Response(
|
||||||
|
status_code=501, content=json.dumps({"error": "task not created"})
|
||||||
|
)
|
||||||
|
|
||||||
tunnel: Optional[TunnelResult] = await queue.get()
|
tunnel: Optional[TunnelResult] = await queue.get()
|
||||||
if tunnel is not None:
|
if tunnel is not None:
|
||||||
self.context.udid = udid
|
self.context.udid = udid
|
||||||
data = {"interface": tunnel.interface, "port": tunnel.port, "address": tunnel.address}
|
data = {
|
||||||
|
"interface": tunnel.interface,
|
||||||
|
"port": tunnel.port,
|
||||||
|
"address": tunnel.address,
|
||||||
|
}
|
||||||
return generate_http_response(data)
|
return generate_http_response(data)
|
||||||
else:
|
else:
|
||||||
return fastapi.Response(
|
return fastapi.Response(
|
||||||
status_code=404, content=json.dumps({"error": "something went wrong during tunnel creation"})
|
status_code=404,
|
||||||
|
content=json.dumps(
|
||||||
|
{"error": "something went wrong during tunnel creation"}
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@self.context.sio.event
|
@self.context.sio.event
|
||||||
@@ -477,49 +614,90 @@ class TunneldRunnerSio:
|
|||||||
@self.context.sio.event
|
@self.context.sio.event
|
||||||
async def message(sid, data):
|
async def message(sid, data):
|
||||||
logger.info("Received message from %s: %s", data, sid)
|
logger.info("Received message from %s: %s", data, sid)
|
||||||
await self.context.sio.emit("message", f"Received message from {sid}: {data}", namespace="/")
|
await self.context.sio.emit(
|
||||||
|
"message", f"Received message from {sid}: {data}", namespace="/"
|
||||||
|
)
|
||||||
|
|
||||||
@self.context.sio.event
|
@self.context.sio.event
|
||||||
async def simulate_location(sid, data):
|
async def simulate_location(sid, data):
|
||||||
logger.info("Simulate location request from %s: %s", sid, data)
|
logger.info("Simulate location request from %s: %s", sid, data)
|
||||||
latitude = data.get("latitude") if isinstance(data, dict) else getattr(data, "latitude", None)
|
latitude = (
|
||||||
longitude = data.get("longitude") if isinstance(data, dict) else getattr(data, "longitude", None)
|
data.get("latitude")
|
||||||
delay = data.get("delay", 0) if isinstance(data, dict) else getattr(data, "delay", 0)
|
if isinstance(data, dict)
|
||||||
|
else getattr(data, "latitude", None)
|
||||||
|
)
|
||||||
|
longitude = (
|
||||||
|
data.get("longitude")
|
||||||
|
if isinstance(data, dict)
|
||||||
|
else getattr(data, "longitude", None)
|
||||||
|
)
|
||||||
|
delay = (
|
||||||
|
data.get("delay", 0)
|
||||||
|
if isinstance(data, dict)
|
||||||
|
else getattr(data, "delay", 0)
|
||||||
|
)
|
||||||
if latitude is not None and longitude is not None:
|
if latitude is not None and longitude is not None:
|
||||||
logger.info("Adding location %s, %s with %ss delay to the queue", latitude, longitude, delay)
|
logger.info(
|
||||||
|
"Adding location %s, %s with %ss delay to the queue",
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
delay,
|
||||||
|
)
|
||||||
await self.context.queue.put((latitude, longitude, delay))
|
await self.context.queue.put((latitude, longitude, delay))
|
||||||
await self.context.sio.emit("simulation", {"status": self.context.simulation_active,
|
await self.context.sio.emit(
|
||||||
"data": {"latitude": self.context.latitude,
|
"simulation",
|
||||||
"cur_longitude": longitude, "next_move": delay}},
|
{
|
||||||
namespace="/")
|
"status": self.context.simulation_active,
|
||||||
|
"data": {
|
||||||
|
"latitude": self.context.latitude,
|
||||||
|
"cur_longitude": longitude,
|
||||||
|
"next_move": delay,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
namespace="/",
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.warning("Invalid location data received from %s: %s", sid, data)
|
logger.warning("Invalid location data received from %s: %s", sid, data)
|
||||||
await self.context.sio.emit("error", "Invalid location data", namespace="/")
|
await self.context.sio.emit(
|
||||||
|
"error", "Invalid location data", namespace="/"
|
||||||
|
)
|
||||||
|
|
||||||
@self.context.sio.event
|
@self.context.sio.event
|
||||||
async def start_simulate_location(sid, data):
|
async def start_simulate_location(sid, data):
|
||||||
logger.info("Start location simulation request from %s", sid)
|
logger.info("Start location simulation request from %s", sid)
|
||||||
if isinstance(data, dict) and data.get("udid"):
|
if isinstance(data, dict) and data.get("udid"):
|
||||||
self.context.udid = data["udid"]
|
self.context.udid = data["udid"]
|
||||||
if self.context.simulation_task is None or self.context.simulation_task.done():
|
if (
|
||||||
|
self.context.simulation_task is None
|
||||||
|
or self.context.simulation_task.done()
|
||||||
|
):
|
||||||
self.context.simulation_active = True
|
self.context.simulation_active = True
|
||||||
self.context.simulation_task = asyncio.create_task(
|
self.context.simulation_task = asyncio.create_task(
|
||||||
start_queue(),
|
start_queue(),
|
||||||
name="location-simulation-worker",
|
name="location-simulation-worker",
|
||||||
)
|
)
|
||||||
await self.context.sio.emit("simulation", {"status": self.context.simulation_active, "data": None},
|
await self.context.sio.emit(
|
||||||
namespace="/")
|
"simulation",
|
||||||
|
{"status": self.context.simulation_active, "data": None},
|
||||||
|
namespace="/",
|
||||||
|
)
|
||||||
|
|
||||||
@self.context.sio.event
|
@self.context.sio.event
|
||||||
async def end_simulate_location(sid, data):
|
async def end_simulate_location(sid, data):
|
||||||
logger.info("End location simulation request from %s", sid)
|
logger.info("End location simulation request from %s", sid)
|
||||||
if self.context.simulation_task is not None and not self.context.simulation_task.done():
|
if (
|
||||||
|
self.context.simulation_task is not None
|
||||||
|
and not self.context.simulation_task.done()
|
||||||
|
):
|
||||||
await self.context.queue.put((None, None, None))
|
await self.context.queue.put((None, None, None))
|
||||||
with suppress(asyncio.CancelledError):
|
with suppress(asyncio.CancelledError):
|
||||||
await self.context.simulation_task
|
await self.context.simulation_task
|
||||||
await empty_queue()
|
await empty_queue()
|
||||||
await self.context.sio.emit("simulation", {"status": self.context.simulation_active, "data": None},
|
await self.context.sio.emit(
|
||||||
namespace="/")
|
"simulation",
|
||||||
|
{"status": self.context.simulation_active, "data": None},
|
||||||
|
namespace="/",
|
||||||
|
)
|
||||||
|
|
||||||
@self.context.sio.event
|
@self.context.sio.event
|
||||||
async def disconnect(sid):
|
async def disconnect(sid):
|
||||||
@@ -535,7 +713,9 @@ class TunneldRunnerSio:
|
|||||||
logger.error("Error starting tunneld: %s", e)
|
logger.error("Error starting tunneld: %s", e)
|
||||||
|
|
||||||
def _run_app(self) -> None:
|
def _run_app(self) -> None:
|
||||||
uvicorn.run(self._asgi_app, host=self.host, port=self.port, loop="asyncio", workers=1)
|
uvicorn.run(
|
||||||
|
self._asgi_app, host=self.host, port=self.port, loop="asyncio", workers=1
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class LocationSimulationQueue(LocationSimulation):
|
class LocationSimulationQueue(LocationSimulation):
|
||||||
@@ -543,26 +723,52 @@ class LocationSimulationQueue(LocationSimulation):
|
|||||||
super().__init__(dvt)
|
super().__init__(dvt)
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
async def play_queue(self, disable_sleep: bool = False, timing_randomness_range: int = 0) -> None:
|
async def play_queue(
|
||||||
|
self, disable_sleep: bool = False, timing_randomness_range: int = 0
|
||||||
|
) -> None:
|
||||||
while True:
|
while True:
|
||||||
latitude, longitude, delay = await self.context.queue.get()
|
latitude, longitude, delay = await self.context.queue.get()
|
||||||
if (latitude, longitude, delay) == (None, None, None):
|
if (latitude, longitude, delay) == (None, None, None):
|
||||||
break
|
break
|
||||||
if delay > 0 and not disable_sleep:
|
if delay > 0 and not disable_sleep:
|
||||||
if timing_randomness_range > 0:
|
if timing_randomness_range > 0:
|
||||||
delay = delay + random.uniform(-timing_randomness_range, timing_randomness_range)
|
delay = delay + random.uniform(
|
||||||
|
-timing_randomness_range, timing_randomness_range
|
||||||
|
)
|
||||||
for i in range(delay, 0, -1):
|
for i in range(delay, 0, -1):
|
||||||
await self.context.sio.emit("simulation", {"status": self.context.simulation_active,
|
self.context.next_move = i
|
||||||
"data": {"latitude": self.context.latitude,
|
await self.context.sio.emit(
|
||||||
"longitude": self.context.longitude,
|
"simulation",
|
||||||
"next_move": i}}, namespace="/")
|
{
|
||||||
|
"status": self.context.simulation_active,
|
||||||
|
"data": {
|
||||||
|
"latitude": self.context.latitude,
|
||||||
|
"longitude": self.context.longitude,
|
||||||
|
"next_move": i,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
namespace="/",
|
||||||
|
)
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
await self.set(latitude, longitude)
|
await self.set(latitude, longitude)
|
||||||
self.context.latitude = latitude
|
self.context.latitude = latitude
|
||||||
self.context.longitude = longitude
|
self.context.longitude = longitude
|
||||||
await self.context.sio.emit("simulation", {"status": self.context.simulation_active,
|
await self.context.sio.emit(
|
||||||
"data": {"latitude": self.context.latitude,
|
"simulation",
|
||||||
"longitude": self.context.longitude,
|
{
|
||||||
"next_move": None}}, namespace="/")
|
"status": self.context.simulation_active,
|
||||||
logger.info("Set simulated location to %s, %s after %ss delay", latitude, longitude, delay)
|
"data": {
|
||||||
|
"latitude": self.context.latitude,
|
||||||
|
"longitude": self.context.longitude,
|
||||||
|
"next_move": None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
namespace="/",
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"Set simulated location to %s, %s after %ss delay",
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
delay,
|
||||||
|
)
|
||||||
self.context.queue.task_done()
|
self.context.queue.task_done()
|
||||||
|
|||||||
Reference in New Issue
Block a user