Skip to content

Commit

Permalink
Merge pull request #86 from artehe/usbmux-tweaks
Browse files Browse the repository at this point in the history
Usbmux tweaks
  • Loading branch information
artehe authored Feb 17, 2025
2 parents 67993b7 + 89318db commit d4546c9
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 54 deletions.
38 changes: 24 additions & 14 deletions Netimobiledevice/Lockdown/LockdownClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public abstract class LockdownClient : LockdownServiceProvider, IDisposable
private ushort _port;
private string _sessionId;
private string _systemBuid;
private readonly UsbmuxdConnectionType _usbmuxdConnectionType;

protected readonly DirectoryInfo? _pairingRecordsCacheDirectory;
/// <summary>
Expand All @@ -43,6 +42,8 @@ public abstract class LockdownClient : LockdownServiceProvider, IDisposable
protected DictionaryNode? _pairRecord;
protected readonly ServiceConnection? _service;

public UsbmuxdConnectionType ConnectionType { get; protected set; }

public string DeviceClass { get; private set; } = LockdownDeviceClass.UNKNOWN;

public string DeviceName => GetValue("DeviceName")?.AsStringNode().Value ?? string.Empty;
Expand Down Expand Up @@ -80,6 +81,7 @@ public bool EnableWifiConnections {
/// <param name="pair_record">Use this pair record instead of the default behavior (search in host/create our own)</param>
/// <param name="pairingRecordsCacheDirectory">Use the following location to search and save pair records</param>
/// <param name="port">lockdownd service port</param>
/// <param name="logger"></param>
protected LockdownClient(ServiceConnection service, string hostId, string identifier = "", string label = DEFAULT_CLIENT_NAME, string systemBuid = SYSTEM_BUID,

Check warning on line 85 in Netimobiledevice/Lockdown/LockdownClient.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable property 'SerialNumber' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
DictionaryNode? pairRecord = null, DirectoryInfo? pairingRecordsCacheDirectory = null, ushort port = SERVICE_PORT, ILogger? logger = null) : base()
{
Expand All @@ -99,20 +101,22 @@ protected LockdownClient(ServiceConnection service, string hostId, string identi
throw new IncorrectModeException();
}

_allValues = GetValue()?.AsDictionaryNode() ?? new DictionaryNode();
_allValues = GetValue()?.AsDictionaryNode() ?? [];

Udid = _allValues["UniqueDeviceID"].AsStringNode().Value;
if (_allValues.TryGetValue("UniqueDeviceID", out PropertyNode? UdidNode)) {
Udid = UdidNode.AsStringNode().Value;
}
ProductType = _allValues["ProductType"].AsStringNode().Value;

if (_allValues.TryGetValue("DevicePublicKey", out PropertyNode? devicePublicKeyNode)) {
_devicePublicKey = devicePublicKeyNode.AsDataNode().Value;
}
else {
_devicePublicKey = Array.Empty<byte>();
_devicePublicKey = [];
}
}

private PropertyNode GetServiceConnectionAttributes(string name, bool useEscrowBag, bool useTrustedConnection)
private DictionaryNode GetServiceConnectionAttributes(string name, bool useEscrowBag, bool useTrustedConnection)
{
if (!IsPaired && useTrustedConnection) {
throw new NotPairedException();
Expand All @@ -138,7 +142,7 @@ private PropertyNode GetServiceConnectionAttributes(string name, bool useEscrowB
return response;
}

private PropertyNode Request(string request, DictionaryNode? options = null, bool verifyRequest = true)
private DictionaryNode Request(string request, DictionaryNode? options = null, bool verifyRequest = true)
{
DictionaryNode message = new DictionaryNode {
{ "Label", new StringNode(_label) },
Expand All @@ -150,10 +154,16 @@ private PropertyNode Request(string request, DictionaryNode? options = null, boo
}
}

DictionaryNode response = _service?.SendReceivePlist(message)?.AsDictionaryNode() ?? new DictionaryNode();
DictionaryNode response = _service?.SendReceivePlist(message)?.AsDictionaryNode() ?? [];

if (verifyRequest && response["Request"].AsStringNode().Value != request) {
throw new LockdownException($"Incorrect response returned, as got {response["Request"].AsStringNode().Value} instead of {request}");
if (verifyRequest && response.TryGetValue("Request", out PropertyNode? requestNode)) {
if (requestNode.AsStringNode().Value != request) {
Logger.LogWarning("Request response did not contain our expected value {value}: {response}", requestNode, response);
throw new LockdownException($"Incorrect response returned, as got {requestNode} instead of {request}");
}
}
else if (verifyRequest) {
throw new LockdownException("Response did not contain the key \"Request\"");
}

if (response.TryGetValue("Error", out PropertyNode? errorNode)) {
Expand Down Expand Up @@ -188,7 +198,7 @@ private DictionaryNode RequestPair(DictionaryNode pairOptions)

private LockdownError Pair()
{
_devicePublicKey = GetValue(null, "DevicePublicKey")?.AsDataNode().Value ?? Array.Empty<byte>();
_devicePublicKey = GetValue(null, "DevicePublicKey")?.AsDataNode().Value ?? [];
if (_devicePublicKey == null || _devicePublicKey.Length == 0) {
_logger.LogDebug("Unable to retrieve DevicePublicKey");
_service?.Close();
Expand Down Expand Up @@ -310,7 +320,7 @@ private bool ValidatePairing()
IsPaired = true;

// Reload data after pairing
_allValues = GetValue()?.AsDictionaryNode() ?? new DictionaryNode();
_allValues = GetValue()?.AsDictionaryNode() ?? [];
Udid = _allValues["UniqueDeviceID"].AsStringNode().Value;

return IsPaired;
Expand Down Expand Up @@ -361,7 +371,7 @@ public void Dispose()

public override PropertyNode? GetValue(string? domain, string? key)
{
DictionaryNode options = new DictionaryNode();
DictionaryNode options = [];
if (!string.IsNullOrEmpty(domain)) {
options.Add("Domain", new StringNode(domain));
}
Expand Down Expand Up @@ -488,7 +498,7 @@ public virtual bool PairDevice()
}

// Now we are paied, reload data
_allValues = GetValue()?.AsDictionaryNode() ?? new DictionaryNode();
_allValues = GetValue()?.AsDictionaryNode() ?? [];
Udid = _allValues["UniqueDeviceID"].AsStringNode().Value;
return IsPaired;
}
Expand All @@ -505,7 +515,7 @@ public virtual void SavePairRecord()

public PropertyNode SetValue(string? domain, string? key, PropertyNode value)
{
DictionaryNode options = new DictionaryNode();
DictionaryNode options = [];
if (!string.IsNullOrWhiteSpace(domain)) {
options.Add("Domain", new StringNode(domain));
}
Expand Down
2 changes: 2 additions & 0 deletions Netimobiledevice/Lockdown/TcpLockdownClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.Extensions.Logging;
using Netimobiledevice.Lockdown.Pairing;
using Netimobiledevice.Plist;
using Netimobiledevice.Usbmuxd;
using System.IO;

namespace Netimobiledevice.Lockdown
Expand All @@ -14,6 +15,7 @@ public TcpLockdownClient(ServiceConnection service, string hostId, string hostna
ILogger? logger = null) : base(service, hostId, identifier, label, systemBuid, pairRecord, pairingRecordsCacheDirectory, port, logger)
{
_hostname = hostname;
ConnectionType = UsbmuxdConnectionType.Network;
}

public override ServiceConnection CreateServiceConnection(ushort port)
Expand Down
2 changes: 2 additions & 0 deletions Netimobiledevice/Lockdown/UsbmuxLockdownClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.Extensions.Logging;
using Netimobiledevice.Lockdown.Pairing;
using Netimobiledevice.Plist;
using Netimobiledevice.Usbmuxd;
using System.IO;

namespace Netimobiledevice.Lockdown
Expand All @@ -14,6 +15,7 @@ public UsbmuxLockdownClient(ServiceConnection service, string hostId, string ide
: base(service, hostId, identifier, label, systemBuid, pairRecord, pairingRecordsCacheDirectory, port, logger)
{
_usbmuxAddress = usbmuxAddress;
ConnectionType = UsbmuxdConnectionType.Usb;
}

public override ServiceConnection CreateServiceConnection(ushort port)
Expand Down
6 changes: 3 additions & 3 deletions Netimobiledevice/MobileDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ public static UsbmuxLockdownClient CreateUsingUsbmux(string serial = "", string
/// <returns>RemoteLockdownClient instance</returns>
public static RemoteLockdownClient CreateUsingRemote(ServiceConnection service, string? identifier = null, string label = LockdownClient.DEFAULT_CLIENT_NAME,
bool autopair = true, float? pairTimeout = null, string? localHostname = null, DictionaryNode? pairRecord = null, string pairingRecordsCacheDir = "",
ushort port = LockdownClient.SERVICE_PORT)
ushort port = LockdownClient.SERVICE_PORT, ILogger? logger = null)
{
RemoteLockdownClient client = RemoteLockdownClient.Create(service, identifier, label: label, localHostname: localHostname, pairRecord: pairRecord,

Check warning on line 74 in Netimobiledevice/MobileDevice.cs

View workflow job for this annotation

GitHub Actions / test

Possible null reference argument for parameter 'identifier' in 'RemoteLockdownClient RemoteLockdownClient.Create(ServiceConnection service, string identifier = "", string systemBuid = "30142955-444094379208051516", string label = "Netimobiledevice", bool autopair = true, float? pairTimeout = null, string localHostname = "", DictionaryNode? pairRecord = null, string pairingRecordsCacheFolder = "", ushort port = 62078, ILogger? logger = null)'.

Check warning on line 74 in Netimobiledevice/MobileDevice.cs

View workflow job for this annotation

GitHub Actions / test

Possible null reference argument for parameter 'localHostname' in 'RemoteLockdownClient RemoteLockdownClient.Create(ServiceConnection service, string identifier = "", string systemBuid = "30142955-444094379208051516", string label = "Netimobiledevice", bool autopair = true, float? pairTimeout = null, string localHostname = "", DictionaryNode? pairRecord = null, string pairingRecordsCacheFolder = "", ushort port = 62078, ILogger? logger = null)'.

Check warning on line 74 in Netimobiledevice/MobileDevice.cs

View workflow job for this annotation

GitHub Actions / deploy

Possible null reference argument for parameter 'identifier' in 'RemoteLockdownClient RemoteLockdownClient.Create(ServiceConnection service, string identifier = "", string systemBuid = "30142955-444094379208051516", string label = "Netimobiledevice", bool autopair = true, float? pairTimeout = null, string localHostname = "", DictionaryNode? pairRecord = null, string pairingRecordsCacheFolder = "", ushort port = 62078, ILogger? logger = null)'.

Check warning on line 74 in Netimobiledevice/MobileDevice.cs

View workflow job for this annotation

GitHub Actions / deploy

Possible null reference argument for parameter 'localHostname' in 'RemoteLockdownClient RemoteLockdownClient.Create(ServiceConnection service, string identifier = "", string systemBuid = "30142955-444094379208051516", string label = "Netimobiledevice", bool autopair = true, float? pairTimeout = null, string localHostname = "", DictionaryNode? pairRecord = null, string pairingRecordsCacheFolder = "", ushort port = 62078, ILogger? logger = null)'.
pairingRecordsCacheFolder: pairingRecordsCacheDir, pairTimeout: pairTimeout, autopair: autopair, port: port);
pairingRecordsCacheFolder: pairingRecordsCacheDir, pairTimeout: pairTimeout, autopair: autopair, port: port, logger: logger);
return client;
}

Expand All @@ -96,7 +96,7 @@ public static TcpLockdownClient CreateUsingTcp(string hostname, string identifie
{
ServiceConnection service = ServiceConnection.CreateUsingTcp(hostname, port, logger);
TcpLockdownClient client = TcpLockdownClient.Create(service, identifier: identifier, label: label, localHostname: localHostname, pairRecord: pairRecord,
pairingRecordsCacheFolder: pairingRecordsCacheDir, pairTimeout: pairTimeout, autopair: autopair, port: port, hostname: hostname);
pairingRecordsCacheFolder: pairingRecordsCacheDir, pairTimeout: pairTimeout, autopair: autopair, port: port, hostname: hostname, logger: logger);
return client;
}
}
Expand Down
11 changes: 4 additions & 7 deletions Netimobiledevice/Usbmuxd/PlistMuxConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,21 @@
using Netimobiledevice.Usbmuxd.Responses;
using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Netimobiledevice.Usbmuxd
{
internal class PlistMuxConnection : UsbmuxConnection
internal class PlistMuxConnection(UsbmuxdSocket sock, ILogger logger) : UsbmuxConnection(sock, UsbmuxdVersion.Plist, logger)
{
private const string PLIST_CLIENT_VERSION_STRING = "1.0.0.0";
private const int PLIST_USBMUX_VERSION = 3;

public PlistMuxConnection(UsbmuxdSocket sock, ILogger logger) : base(sock, UsbmuxdVersion.Plist, logger) { }

private static PropertyNode CreatePlistMessage(string messageType)
private static DictionaryNode CreatePlistMessage(string messageType)
{
string bundleId = GetBundleId();
string assemblyName = GetAssemblyName();

DictionaryNode plistDict = new DictionaryNode();
DictionaryNode plistDict = [];
if (!string.IsNullOrWhiteSpace(bundleId)) {
plistDict.Add("BundleID", new StringNode(bundleId));
}
Expand Down Expand Up @@ -152,7 +149,7 @@ public void SavePairRecord(string identifier, ulong deviceId, byte[] recordData)

public int Send(PropertyNode msg)
{
byte[] payload = PropertyList.SaveAsByteArray(msg, PlistFormat.Xml).ToArray();
byte[] payload = [.. PropertyList.SaveAsByteArray(msg, PlistFormat.Xml)];
return SendPacket(UsbmuxdMessageType.Plist, Tag, payload);
}

Expand Down
57 changes: 27 additions & 30 deletions Netimobiledevice/Usbmuxd/UsbmuxConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Netimobiledevice.Usbmuxd
{
internal abstract class UsbmuxConnection : IDisposable
internal abstract class UsbmuxConnection(UsbmuxdSocket socket, UsbmuxdVersion protocolVersion, ILogger logger) : IDisposable
{
protected const int DEFAULT_CONNECTION_TIMEOUT = 5000;

Expand All @@ -19,30 +19,21 @@ internal abstract class UsbmuxConnection : IDisposable
/// residing inside the target device. when this happens, we can no longer send/receive control commands to
/// usbmux on same socket
/// </summary>
private bool connected;
/// <summary>
/// The internal logger
/// </summary>
private readonly ILogger logger;
private bool _connected;

protected int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;

protected UsbmuxdSocket Sock { get; }
/// <summary>
/// The internal logger
/// </summary>
protected ILogger Logger { get; } = logger;
protected UsbmuxdSocket Sock { get; } = socket;
/// <summary>
/// Message sequence number. Used when verifying the response matched the request
/// </summary>
protected int Tag { get; set; }
public UsbmuxdVersion ProtocolVersion { get; }
public List<UsbmuxdDevice> Devices { get; private set; } = new List<UsbmuxdDevice>();

protected UsbmuxConnection(UsbmuxdSocket socket, UsbmuxdVersion protocolVersion, ILogger logger)
{
this.logger = logger;

ProtocolVersion = protocolVersion;
Sock = socket;
Tag = 1;
}
protected int Tag { get; set; } = 1;
public UsbmuxdVersion ProtocolVersion { get; } = protocolVersion;
public List<UsbmuxdDevice> Devices { get; private set; } = [];

public void Dispose()
{
Expand All @@ -53,7 +44,7 @@ private int ReceivePacket(out UsbmuxdHeader header, out byte[] payload)
{
AssertNotConnected();

payload = Array.Empty<byte>();
payload = [];
header = new UsbmuxdHeader() {
Length = 0,
Message = 0,
Expand All @@ -65,16 +56,16 @@ private int ReceivePacket(out UsbmuxdHeader header, out byte[] payload)
byte[] headerBuffer = Sock.Receive(Marshal.SizeOf(header));
int recievedLength = headerBuffer.Length;
if (recievedLength < 0) {
logger.LogError("Error receiving packet: {recievedLength}", recievedLength);
Logger.LogError("Error receiving packet: {recievedLength}", recievedLength);
return recievedLength;
}
if (recievedLength < Marshal.SizeOf(header)) {
logger.LogError("Received packet is too small, got {recievedLength} bytes!", recievedLength);
Logger.LogError("Received packet is too small, got {recievedLength} bytes!", recievedLength);
return recievedLength;
}

header = StructExtentions.FromBytes<UsbmuxdHeader>(headerBuffer);
byte[] payloadLoc = Array.Empty<byte>();
byte[] payloadLoc = [];

int payloadSize = header.Length - Marshal.SizeOf(header);
if (payloadSize > 0) {
Expand All @@ -89,7 +80,7 @@ private int ReceivePacket(out UsbmuxdHeader header, out byte[] payload)
rsize += (uint) res;
} while (rsize < payloadSize);
if (rsize != payloadSize) {
logger.LogError("Error receiving payload of size {payloadSize} (bytes received: {rsize})", payloadSize, rsize);
Logger.LogError("Error receiving payload of size {payloadSize} (bytes received: {rsize})", payloadSize, rsize);
throw new UsbmuxException("Bad Message");
}
}
Expand All @@ -104,7 +95,7 @@ private int ReceivePacket(out UsbmuxdHeader header, out byte[] payload)
/// <exception cref="UsbmuxException"></exception>
protected void AssertNotConnected()
{
if (connected) {
if (_connected) {
throw new UsbmuxException("Usbmux is connected, cannot issue control packets");
}
}
Expand Down Expand Up @@ -141,16 +132,22 @@ protected int SendPacket(UsbmuxdMessageType message, int tag, byte[] payload)

int sent = Sock.Send(header.GetBytes());
if (sent != Marshal.SizeOf(header)) {
logger.LogError($"ERROR: could not send packet header");
Logger.LogError($"ERROR: could not send packet header");
return -1;
}

if (payload != null && payload.Length > 0) {
int res = Sock.Send(payload);
sent += res;
int res = 0;
try {
res = Sock.Send(payload);
sent += res;
}
catch (SocketException ex) {
throw new UsbmuxException($"Failed to send {payload.Length} bytes; actually sent {res}", ex);
}
}
if (sent != header.Length) {
logger.LogError("ERROR: could not send whole packet (sent {sent} of {length})", sent, header.Length);
Logger.LogError("Could not send whole packet (sent {sent} of {length})", sent, header.Length);
Sock.Close();
return -1;
}
Expand All @@ -176,7 +173,7 @@ public void Close()
public Socket Connect(UsbmuxdDevice device, ushort port)
{
RequestConnect(device.DeviceId, port);
connected = true;
_connected = true;
return Sock.GetInternalSocket();
}

Expand Down
Loading

0 comments on commit d4546c9

Please sign in to comment.