diff --git a/src/fruity/device-monitor-macos.vala b/src/fruity/device-monitor-macos.vala index 974fd7680..24ecf96b0 100644 --- a/src/fruity/device-monitor-macos.vala +++ b/src/fruity/device-monitor-macos.vala @@ -246,6 +246,12 @@ namespace Frida.Fruity { } } + public int64 opened_at { + get { + return _opened_at; + } + } + public Darwin.Xpc.Uuid? assertion_identifier { get { return _assertion_identifier; @@ -256,6 +262,7 @@ namespace Frida.Fruity { private Darwin.Xpc.Uuid? _assertion_identifier; private InetAddress? tunnel_device_address; private DiscoveryService? _discovery; + private int64 _opened_at = -1; public MacOSTunnel (XpcClient pairing_device) { this.pairing_device = pairing_device; @@ -275,6 +282,8 @@ namespace Frida.Fruity { r.body.set_int64 ("flags", 0); var response = yield pairing_device.request (r.message, cancellable); + _opened_at = get_monotonic_time (); + var reader = new XpcObjectReader (response); reader.read_member ("response"); diff --git a/src/fruity/device-monitor.vala b/src/fruity/device-monitor.vala index adf41075c..2448615de 100644 --- a/src/fruity/device-monitor.vala +++ b/src/fruity/device-monitor.vala @@ -492,6 +492,10 @@ namespace Frida.Fruity { get; } + public abstract int64 opened_at { + get; + } + public abstract async void close (Cancellable? cancellable) throws IOError; public abstract async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError; } @@ -1635,9 +1639,16 @@ namespace Frida.Fruity { } } + public int64 opened_at { + get { + return _opened_at; + } + } + private UsbNcmDriver? ncm; private TunnelConnection? tunnel_connection; private DiscoveryService? _discovery_service; + private int64 _opened_at = -1; public PortableUsbTunnel (UsbDevice device, NcmPeer peer, PairingStore store) { Object ( @@ -1670,6 +1681,8 @@ namespace Frida.Fruity { TunnelConnection tc = yield pairing_service.open_tunnel (ncm_peer.ip, netstack, cancellable); tc.closed.connect (on_tunnel_connection_close); + _opened_at = get_monotonic_time (); + var rsd_endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress), address: tc.remote_address, port: tc.remote_rsd_port, @@ -1831,8 +1844,15 @@ namespace Frida.Fruity { } } + public int64 opened_at { + get { + return _opened_at; + } + } + private TunnelConnection? tunnel_connection; private DiscoveryService? _discovery_service; + private int64 _opened_at = -1; private const uint PAIRING_CONNECTION_TIMEOUT = 2000; @@ -1854,6 +1874,8 @@ namespace Frida.Fruity { TunnelConnection tc = yield pairing_service.open_tunnel (endpoint.get_address (), netstack, cancellable); + _opened_at = get_monotonic_time (); + var rsd_endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress), address: tc.remote_address, port: tc.remote_rsd_port, diff --git a/src/fruity/fruity-host-session.vala b/src/fruity/fruity-host-session.vala index fe2c6df4c..d85be1483 100644 --- a/src/fruity/fruity-host-session.vala +++ b/src/fruity/fruity-host-session.vala @@ -1087,8 +1087,7 @@ namespace Frida { DBusConnection? connection = null; try { - var channel = - yield device.open_tcp_channel (DEFAULT_CONTROL_PORT.to_string (), ALLOW_ANY_TRANSPORT, cancellable); + var channel = yield connect_to_remote_server (cancellable); IOStream stream = channel.stream; WebServiceTransport transport = PLAIN; @@ -1157,6 +1156,51 @@ namespace Frida { } } + private async Fruity.TcpChannel connect_to_remote_server (Cancellable? cancellable) throws Error, IOError { + var tunnel = yield device.find_tunnel (cancellable); + bool tunnel_recently_opened = tunnel != null && get_monotonic_time () - tunnel.opened_at < 1000000; + + uint delays[] = { 0, 50, 250 }; + uint max_attempts = tunnel_recently_opened ? delays.length : 1; + var main_context = MainContext.ref_thread_default (); + + Error? pending_error = null; + for (uint attempts = 0; attempts != max_attempts; attempts++) { + uint delay = delays[attempts]; + if (delay != 0) { + var timeout_source = new TimeoutSource (delay); + timeout_source.set_callback (connect_to_remote_server.callback); + timeout_source.attach (main_context); + + var cancel_source = new CancellableSource (cancellable); + cancel_source.set_callback (connect_to_remote_server.callback); + cancel_source.attach (main_context); + + yield; + + cancel_source.destroy (); + timeout_source.destroy (); + + if (cancellable.is_cancelled ()) + break; + } + + bool is_last_attempt = attempts == max_attempts - 1; + var open_flags = is_last_attempt + ? Fruity.OpenTcpChannelFlags.ALLOW_ANY_TRANSPORT + : Fruity.OpenTcpChannelFlags.ALLOW_TUNNEL; + + try { + return yield device.open_tcp_channel (DEFAULT_CONTROL_PORT.to_string (), open_flags, cancellable); + } catch (Error e) { + pending_error = e; + if (!(e is Error.SERVER_NOT_RUNNING)) + break; + } + } + throw pending_error; + } + private void attach_remote_server (RemoteServer server) { server.connection.on_closed.connect (on_remote_connection_closed);