From 1bd9d389b8a3e1e56b92222c4adc9169fe259b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Andr=C3=A9=20Vadla=20Ravn=C3=A5s?= Date: Tue, 26 Nov 2024 19:39:37 +0100 Subject: [PATCH] fruity: Fix direct channel reliability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Where we might establish the HostSession connection through usbmuxd, and proceed to establish direct connections through the tunnel. This is usually fine, except when an Apple service happens to bind to the same port inside the tunnel. In that case we'd end up talking to that instead of the broker port, which is listening on INADDR_ANY. Kudos to @mrmacete for helping track this one down. Co-authored-by: Håvard Sørbø --- src/fruity/device-monitor.vala | 150 ++++++++++++++++------------ src/fruity/fruity-host-session.vala | 42 ++++++-- 2 files changed, 120 insertions(+), 72 deletions(-) diff --git a/src/fruity/device-monitor.vala b/src/fruity/device-monitor.vala index 091574fd9..adf41075c 100644 --- a/src/fruity/device-monitor.vala +++ b/src/fruity/device-monitor.vala @@ -324,87 +324,96 @@ namespace Frida.Fruity { unowned string location = tokens[1]; if (protocol == "tcp") { - var tunnel = yield find_tunnel (cancellable); - - uint16 port; - ulong raw_port; - if (ulong.try_parse (location, out raw_port)) { - if (raw_port == 0 || raw_port > uint16.MAX) - throw new Error.INVALID_ARGUMENT ("Invalid TCP port"); - port = (uint16) raw_port; - } else { - if (tunnel == null) - throw new Error.NOT_SUPPORTED ("Unable to resolve port name; tunnel not available"); - var service_info = tunnel.discovery.get_service (location); - port = service_info.port; - } - - Error? pending_error = null; - - if (tunnel != null) { - try { - return yield tunnel.open_tcp_connection (port, cancellable); - } catch (Error e) { - if (e is Error.SERVER_NOT_RUNNING) - pending_error = e; - else - throw e; - } - } + var channel = yield open_tcp_channel (location, ALLOW_ANY_TRANSPORT, cancellable); + return channel.stream; + } - var usbmux_device = find_usbmux_device (); - if (usbmux_device != null) { - if (usbmux_device.connection_type == USB) { - UsbmuxClient client = null; - try { - client = yield UsbmuxClient.open (cancellable); + if (protocol == "lockdown") + return yield open_lockdown_service (location, cancellable); - yield client.connect_to_port (usbmux_device.id, port, cancellable); + throw new Error.NOT_SUPPORTED ("Unsupported channel address"); + } - return client.connection; - } catch (GLib.Error e) { - if (client != null) - client.close.begin (); + public async TcpChannel open_tcp_channel (string location, OpenTcpChannelFlags flags, Cancellable? cancellable) + throws Error, IOError { + var usbmux_device = find_usbmux_device (); + var tunnel = yield find_tunnel (cancellable); - if (e is UsbmuxError.CONNECTION_REFUSED) - throw new Error.SERVER_NOT_RUNNING ("%s", e.message); + uint16 port; + ulong raw_port; + if (ulong.try_parse (location, out raw_port)) { + if (raw_port == 0 || raw_port > uint16.MAX) + throw new Error.INVALID_ARGUMENT ("Invalid TCP port"); + port = (uint16) raw_port; + } else { + if (tunnel == null) + throw new Error.NOT_SUPPORTED ("Unable to resolve port name; tunnel not available"); + if ((flags & OpenTcpChannelFlags.ALLOW_TUNNEL) == 0) + throw new Error.NOT_SUPPORTED ("Connection to tunnel service not allowed by flags"); + var service_info = tunnel.discovery.get_service (location); + port = service_info.port; + } - throw new Error.TRANSPORT ("%s", e.message); - } - } + Error? pending_error = null; - InetSocketAddress device_address = usbmux_device.network_address; - var target_address = (InetSocketAddress) Object.new (typeof (InetSocketAddress), - address: device_address.address, - port: port, - flowinfo: device_address.flowinfo, - scope_id: device_address.scope_id - ); + if ((flags & OpenTcpChannelFlags.ALLOW_TUNNEL) != 0 && tunnel != null) { + try { + var stream = yield tunnel.open_tcp_connection (port, cancellable); + return new TcpChannel () { stream = stream, kind = TUNNEL }; + } catch (Error e) { + if (e is Error.SERVER_NOT_RUNNING) + pending_error = e; + else + throw e; + } + } - var client = new SocketClient (); + if ((flags & OpenTcpChannelFlags.ALLOW_USBMUX) != 0 && usbmux_device != null) { + if (usbmux_device.connection_type == USB) { + UsbmuxClient client = null; try { - var connection = yield client.connect_async (target_address, cancellable); + client = yield UsbmuxClient.open (cancellable); - Tcp.enable_nodelay (connection.socket); + yield client.connect_to_port (usbmux_device.id, port, cancellable); - return connection; + return new TcpChannel () { stream = client.connection, kind = USBMUX }; } catch (GLib.Error e) { - if (e is IOError.CONNECTION_REFUSED) + if (client != null) + client.close.begin (); + + if (e is UsbmuxError.CONNECTION_REFUSED) throw new Error.SERVER_NOT_RUNNING ("%s", e.message); throw new Error.TRANSPORT ("%s", e.message); } } - if (pending_error != null) - throw pending_error; - throw new Error.TRANSPORT ("No viable transport found"); - } + InetSocketAddress device_address = usbmux_device.network_address; + var target_address = (InetSocketAddress) Object.new (typeof (InetSocketAddress), + address: device_address.address, + port: port, + flowinfo: device_address.flowinfo, + scope_id: device_address.scope_id + ); - if (protocol == "lockdown") - return yield open_lockdown_service (location, cancellable); + var client = new SocketClient (); + try { + var connection = yield client.connect_async (target_address, cancellable); - throw new Error.NOT_SUPPORTED ("Unsupported channel address"); + Tcp.enable_nodelay (connection.socket); + + return new TcpChannel () { stream = connection, kind = USBMUX }; + } catch (GLib.Error e) { + if (e is IOError.CONNECTION_REFUSED) + throw new Error.SERVER_NOT_RUNNING ("%s", e.message); + + throw new Error.TRANSPORT ("%s", e.message); + } + } + + if (pending_error != null) + throw pending_error; + throw new Error.TRANSPORT ("No viable transport found"); } private static int compare_transports (Transport a, Transport b) { @@ -432,6 +441,23 @@ namespace Frida.Fruity { } } + public class TcpChannel { + public IOStream stream; + public Kind kind; + + public enum Kind { + USBMUX, + TUNNEL + } + } + + [Flags] + public enum OpenTcpChannelFlags { + ALLOW_USBMUX, + ALLOW_TUNNEL, + ALLOW_ANY_TRANSPORT = ALLOW_USBMUX | ALLOW_TUNNEL, + } + public interface Transport : Object { public abstract ConnectionType connection_type { get; diff --git a/src/fruity/fruity-host-session.vala b/src/fruity/fruity-host-session.vala index 56c192d6e..fe2c6df4c 100644 --- a/src/fruity/fruity-host-session.vala +++ b/src/fruity/fruity-host-session.vala @@ -971,7 +971,7 @@ namespace Frida { var transport_broker = server.transport_broker; if (transport_broker != null) { try { - entry.connection = yield establish_direct_connection (transport_broker, remote_session_id, device, + entry.connection = yield establish_direct_connection (transport_broker, remote_session_id, server, cancellable); } catch (Error e) { if (e is Error.NOT_SUPPORTED) @@ -1087,10 +1087,10 @@ namespace Frida { DBusConnection? connection = null; try { - var stream = yield device.open_channel ( - ("tcp:%" + uint16.FORMAT_MODIFIER + "u").printf (DEFAULT_CONTROL_PORT), - cancellable); + var channel = + yield device.open_tcp_channel (DEFAULT_CONTROL_PORT.to_string (), ALLOW_ANY_TRANSPORT, cancellable); + IOStream stream = channel.stream; WebServiceTransport transport = PLAIN; string? origin = null; @@ -1118,7 +1118,7 @@ namespace Frida { if (connection.closed) throw new Error.SERVER_NOT_RUNNING ("Unable to connect to remote frida-server"); - var server = new RemoteServer (session, connection, flavor, transport_broker); + var server = new RemoteServer (flavor, session, connection, channel, device, transport_broker); attach_remote_server (server); current_remote_server = server; last_server_check_timer = null; @@ -1452,7 +1452,12 @@ namespace Frida { } } - private class RemoteServer : Object { + private class RemoteServer : Object, HostChannelProvider { + public Flavor flavor { + get; + construct; + } + public HostSession session { get; construct; @@ -1463,7 +1468,12 @@ namespace Frida { construct; } - public Flavor flavor { + public Fruity.TcpChannel channel { + get; + construct; + } + + public Fruity.Device device { get; construct; } @@ -1478,15 +1488,27 @@ namespace Frida { set; } - public RemoteServer (HostSession session, DBusConnection connection, Flavor flavor, - TransportBroker? transport_broker) { + public RemoteServer (Flavor flavor, HostSession session, DBusConnection connection, Fruity.TcpChannel channel, + Fruity.Device device, TransportBroker? transport_broker) { Object ( + flavor: flavor, session: session, connection: connection, - flavor: flavor, + channel: channel, + device: device, transport_broker: transport_broker ); } + + public async IOStream open_channel (string address, Cancellable? cancellable) throws Error, IOError { + if (!address.has_prefix ("tcp:")) + throw new Error.NOT_SUPPORTED ("Unsupported channel address"); + var flags = (channel.kind == TUNNEL) + ? Fruity.OpenTcpChannelFlags.ALLOW_TUNNEL + : Fruity.OpenTcpChannelFlags.ALLOW_USBMUX; + var channel = yield device.open_tcp_channel (address[4:], flags, cancellable); + return channel.stream; + } } }