Skip to content

Commit

Permalink
Merge pull request #57 from artehe/afc-packet-serialise
Browse files Browse the repository at this point in the history
Afc packet serialise
  • Loading branch information
artehe authored Sep 16, 2024
2 parents 01deab9 + 5ad6141 commit c316de3
Show file tree
Hide file tree
Showing 25 changed files with 279 additions and 196 deletions.
7 changes: 0 additions & 7 deletions Netimobiledevice/Afc/AfcFileCloseRequest.cs

This file was deleted.

19 changes: 0 additions & 19 deletions Netimobiledevice/Afc/AfcFileInfoRequest.cs

This file was deleted.

8 changes: 0 additions & 8 deletions Netimobiledevice/Afc/AfcFileReadRequest.cs

This file was deleted.

25 changes: 12 additions & 13 deletions Netimobiledevice/Afc/AfcHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,23 @@ namespace Netimobiledevice.Afc
{
internal class AfcHeader
{
public const string Magic = "CFA6LPAA";
public ulong EntireLength;
public ulong Length;
public ulong PacketNumber;
public AfcOpCode Operation;
private const string MAGIC_STRING = "CFA6LPAA";

public byte[] Magic { get; } = Encoding.UTF8.GetBytes(MAGIC_STRING);
public ulong EntireLength { get; set; }
public ulong Length { get; set; }
public ulong PacketNumber { get; set; }
public AfcOpCode Operation { get; set; }

public static AfcHeader FromBytes(byte[] bytes)
{
using (MemoryStream ms = new MemoryStream(bytes)) {
byte[] magicBytes = Encoding.UTF8.GetBytes(Magic);
byte[] magicBytes = Encoding.UTF8.GetBytes(MAGIC_STRING);
byte[] readMagicBytes = new byte[magicBytes.Length];
ms.Read(readMagicBytes, 0, readMagicBytes.Length);
for (int i = 0; i < magicBytes.Length; i++) {
if (magicBytes[i] != readMagicBytes[i]) {
throw new Exception("Missmatch in magic bytes for afc header");
throw new AfcException("Missmatch in magic bytes for afc header");
}
}
}
Expand All @@ -38,7 +40,7 @@ public static AfcHeader FromBytes(byte[] bytes)
public byte[] GetBytes()
{
List<byte> bytes = new List<byte>();
bytes.AddRange(Encoding.UTF8.GetBytes(Magic));
bytes.AddRange(Magic);
bytes.AddRange(BitConverter.GetBytes(EntireLength));
bytes.AddRange(BitConverter.GetBytes(Length));
bytes.AddRange(BitConverter.GetBytes(PacketNumber));
Expand All @@ -49,11 +51,8 @@ public byte[] GetBytes()

public static int GetSize()
{
int size = Encoding.UTF8.GetBytes(Magic).Length;
size += sizeof(ulong);
size += sizeof(ulong);
size += sizeof(ulong);
size += sizeof(ulong);
int size = Encoding.UTF8.GetBytes(MAGIC_STRING).Length;
size += sizeof(ulong) * 4;
return size;
}
}
Expand Down
8 changes: 0 additions & 8 deletions Netimobiledevice/Afc/AfcLockRequest.cs

This file was deleted.

23 changes: 0 additions & 23 deletions Netimobiledevice/Afc/AfcReadDirectoryRequest.cs

This file was deleted.

19 changes: 0 additions & 19 deletions Netimobiledevice/Afc/AfcRmRequest.cs

This file was deleted.

104 changes: 45 additions & 59 deletions Netimobiledevice/Afc/AfcService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging;
using Netimobiledevice.Afc.Packets;
using Netimobiledevice.Extentions;
using Netimobiledevice.Lockdown;
using Netimobiledevice.Plist;
Expand Down Expand Up @@ -43,29 +44,20 @@ private static string GetServiceName(LockdownServiceProvider lockdown, string pr
return serviceName;
}

private async Task DispatchPacket(AfcOpCode opCode, byte[] data, CancellationToken cancellationToken, ulong? thisLength = null)
private async Task DispatchPacket(AfcOpCode opCode, AfcPacket packet, CancellationToken cancellationToken, ulong? thisLength = null)
{
AfcHeader header = new AfcHeader() {
EntireLength = (ulong) data.LongLength,
Length = (ulong) data.LongLength,
packet.Header = new AfcHeader() {
EntireLength = (ulong) packet.PacketSize,
Length = (ulong) packet.PacketSize,
PacketNumber = _packetNumber,
Operation = opCode
};
header.EntireLength = (ulong) (header.GetBytes().Length + data.Length);
header.Length = (ulong) (header.GetBytes().Length + data.Length);

if (thisLength != null) {
header.Length = (ulong) thisLength;
packet.Header.Length = (ulong) thisLength;
}

_packetNumber++;

List<byte> packet = new List<byte>();
packet.AddRange(header.GetBytes());
packet.AddRange(data);

byte[] packetBytes = packet.ToArray();
await Service.SendAsync(packetBytes, cancellationToken).ConfigureAwait(false);
await Service.SendAsync(packet.GetBytes(), cancellationToken).ConfigureAwait(false);
}

private async Task<byte[]> FileRead(ulong handle, ulong size, CancellationToken cancellationToken)
Expand All @@ -85,8 +77,8 @@ private async Task<byte[]> FileRead(ulong handle, ulong size, CancellationToken
Size = size
};

await DispatchPacket(AfcOpCode.Read, readRequest.GetBytes(), cancellationToken).ConfigureAwait(false);
(AfcError status, byte[] chunk) = ReceiveData();
await DispatchPacket(AfcOpCode.Read, readRequest, cancellationToken).ConfigureAwait(false);
(AfcError status, byte[] chunk) = await ReceiveData(cancellationToken).ConfigureAwait(false);
if (status != AfcError.Success) {
throw new AfcException(status, "File Read Error");
}
Expand All @@ -102,8 +94,8 @@ public async Task<DictionaryNode> GetFileInfo(string filename, CancellationToken
{
Dictionary<string, string> stat;
try {
AfcFileInfoRequest request = new AfcFileInfoRequest(new CString(filename, Encoding.UTF8));
byte[] response = await RunOperation(AfcOpCode.GetFileInfo, request.GetBytes(), cancellationToken).ConfigureAwait(false);
AfcFileInfoRequest request = new AfcFileInfoRequest(filename);
byte[] response = await RunOperation(AfcOpCode.GetFileInfo, request, cancellationToken).ConfigureAwait(false);
stat = ParseFileInfoResponseToDict(response);
}
catch (AfcException ex) {
Expand Down Expand Up @@ -159,24 +151,22 @@ private static Dictionary<string, string> ParseFileInfoResponseToDict(byte[] dat
return result;
}

private async Task<byte[]> RunOperation(AfcOpCode opCode, byte[] data, CancellationToken cancellationToken)
private async Task<byte[]> RunOperation(AfcOpCode opCode, AfcPacket packet, CancellationToken cancellationToken)
{
await DispatchPacket(opCode, data, cancellationToken).ConfigureAwait(false);
(AfcError status, byte[] recievedData) = ReceiveData();

await DispatchPacket(opCode, packet, cancellationToken).ConfigureAwait(false);
(AfcError status, byte[] recievedData) = await ReceiveData(cancellationToken).ConfigureAwait(false);
if (status != AfcError.Success) {
if (status == AfcError.ObjectNotFound) {
throw new AfcFileNotFoundException(AfcError.ObjectNotFound);
}
throw new AfcException($"Afc opcode {opCode} failed with status: {status}");
}

return recievedData;
}

private (AfcError, byte[]) ReceiveData()
private async Task<(AfcError, byte[])> ReceiveData(CancellationToken cancellationToken)
{
byte[] response = Service.Receive(AfcHeader.GetSize());
byte[] response = await Service.ReceiveAsync(AfcHeader.GetSize(), cancellationToken).ConfigureAwait(false);

AfcError status = AfcError.Success;
byte[] data = Array.Empty<byte>();
Expand Down Expand Up @@ -225,10 +215,9 @@ private async Task<string> ResolvePath(string filename, CancellationToken cancel
/// <returns></returns>
private async Task<bool> RmSingle(string filename, CancellationToken cancellationToken, bool force = false)
{
AfcRmRequest request = new AfcRmRequest(new CString(filename, Encoding.UTF8));

AfcRmRequest request = new AfcRmRequest(filename);
try {
await RunOperation(AfcOpCode.RemovePath, request.GetBytes(), cancellationToken).ConfigureAwait(false);
await RunOperation(AfcOpCode.RemovePath, request, cancellationToken).ConfigureAwait(false);
return true;
}
catch (AfcException) {
Expand Down Expand Up @@ -282,21 +271,21 @@ public async Task<byte[]> Lock(ulong handle, AfcLockModes operation, Cancellatio
Handle = handle,
Op = (ulong) operation
};
return await RunOperation(AfcOpCode.FileLock, request.GetBytes(), cancellationToken).ConfigureAwait(false);
return await RunOperation(AfcOpCode.FileLock, request, cancellationToken).ConfigureAwait(false);
}

public async Task<byte[]> FileClose(ulong handle, CancellationToken cancellationToken)
{
AfcFileCloseRequest request = new AfcFileCloseRequest() {
Handle = handle,
};
return await RunOperation(AfcOpCode.FileClose, request.GetBytes(), cancellationToken).ConfigureAwait(false);
return await RunOperation(AfcOpCode.FileClose, request, cancellationToken).ConfigureAwait(false);
}

public async Task<ulong> FileOpen(string filename, CancellationToken cancellationToken, AfcFileOpenMode mode = AfcFileOpenMode.ReadOnly)
{
AfcFileOpenRequest openRequest = new AfcFileOpenRequest(mode, new CString(filename, Encoding.UTF8));
byte[] data = await RunOperation(AfcOpCode.FileOpen, openRequest.GetBytes(), cancellationToken).ConfigureAwait(false);
AfcFileOpenRequest openRequest = new AfcFileOpenRequest(mode, filename);
byte[] data = await RunOperation(AfcOpCode.FileOpen, openRequest, cancellationToken).ConfigureAwait(false);
return StructExtentions.FromBytes<AfcFileOpenResponse>(data).Handle;
}

Expand All @@ -306,19 +295,19 @@ public async Task FileWrite(ulong handle, byte[] data, CancellationToken cancell
int chunksCount = data.Length / chunkSize;
Logger?.LogDebug("Writing {dataSize} bytes in {chunksCount} chunks", dataSize, chunksCount);

byte[] fileHandle = BitConverter.GetBytes(handle);
List<byte> writtenData = new List<byte>();
for (int i = 0; i < chunksCount; i++) {
cancellationToken.ThrowIfCancellationRequested();
Logger?.LogDebug("Writing chunk {i}", i);

byte[] chunk = data.Skip(i * chunkSize).Take(chunkSize).ToArray();
byte[] packet = fileHandle.Concat(chunk).ToArray();

AfcFileWritePacket packet = new AfcFileWritePacket() {
Handle = handle,
Data = data.Skip(i * chunkSize).Take(chunkSize).ToArray()
};
await DispatchPacket(AfcOpCode.Write, packet, cancellationToken, 48).ConfigureAwait(false);
writtenData.AddRange(chunk);
writtenData.AddRange(packet.Data);

(AfcError status, byte[] response) = ReceiveData();
(AfcError status, byte[] _) = await ReceiveData(cancellationToken).ConfigureAwait(false);
if (status != AfcError.Success) {
throw new AfcException(status, $"Failed to write chunk: {status}");
}
Expand All @@ -327,13 +316,14 @@ public async Task FileWrite(ulong handle, byte[] data, CancellationToken cancell

if (dataSize % (ulong) chunkSize > 0) {
Logger?.LogDebug("Writing last chunk");
byte[] chunk = data.Skip(chunksCount * chunkSize).ToArray();
byte[] packet = fileHandle.Concat(chunk).ToArray();

AfcFileWritePacket packet = new AfcFileWritePacket() {
Handle = handle,
Data = data.Skip(chunksCount * chunkSize).ToArray()
};
await DispatchPacket(AfcOpCode.Write, packet, cancellationToken, 48).ConfigureAwait(false);
writtenData.AddRange(chunk);
writtenData.AddRange(packet.Data);

(AfcError status, byte[] response) = ReceiveData();
(AfcError status, byte[] _) = await ReceiveData(cancellationToken).ConfigureAwait(false);
if (status != AfcError.Success) {
throw new AfcException(status, $"Failed to write last chunk: {status}");
}
Expand All @@ -345,8 +335,8 @@ public async Task<List<string>> GetDirectoryList(CancellationToken cancellationT
{
List<string> directoryList = new List<string>();
try {
AfcFileInfoRequest request = new AfcFileInfoRequest(new CString("/", Encoding.UTF8));
byte[] response = await RunOperation(AfcOpCode.ReadDir, request.GetBytes(), cancellationToken).ConfigureAwait(false);
AfcFileInfoRequest request = new AfcFileInfoRequest("/");
byte[] response = await RunOperation(AfcOpCode.ReadDir, request, cancellationToken).ConfigureAwait(false);
directoryList = ParseFileInfoResponseForMessage(response);
}
catch (Exception ex) {
Expand All @@ -357,7 +347,7 @@ public async Task<List<string>> GetDirectoryList(CancellationToken cancellationT

private async Task<List<string>> ListDirectory(string filename, CancellationToken cancellationToken)
{
byte[] data = await RunOperation(AfcOpCode.ReadDir, new AfcReadDirectoryRequest(filename).GetBytes(), cancellationToken);
byte[] data = await RunOperation(AfcOpCode.ReadDir, new AfcReadDirectoryRequest(filename), cancellationToken);
// Make sure to skip "." and ".."
return AfcReadDirectoryResponse.Parse(data).Filenames.Skip(2).ToList();
}
Expand Down Expand Up @@ -438,32 +428,28 @@ public async Task Pull(string relativeSrc, string dst, CancellationToken cancell
src = $"{src}/{relativeSrc}";
}

string[] splitSrc = relativeSrc.Split('/');
string dstPath = splitSrc.Length > 1 ? Path.Combine(dst, splitSrc[^1]) : Path.Combine(dst, relativeSrc);
if (OperatingSystem.IsWindows()) {
// Windows filesystems can't cope with ':' so we replace these with '-'
dstPath = dstPath.Replace(':', '-');
}
Logger?.LogInformation("{src} --> {dst}", src, dst);

src = await ResolvePath(src, cancellationToken).ConfigureAwait(false);

if (!await IsDir(src, cancellationToken).ConfigureAwait(false)) {
// Normal file
if (Path.EndsInDirectorySeparator(dst)) {
string[] splitSrc = relativeSrc.Split('/');
string filename = splitSrc[^1];
dst = Path.Combine(dst, filename);
}
using (FileStream fs = new FileStream(dst, FileMode.Create)) {
using (FileStream fs = new FileStream(dstPath, FileMode.Create)) {
byte[]? data = await GetFileContents(src, cancellationToken).ConfigureAwait(false);
await fs.WriteAsync(data, cancellationToken).ConfigureAwait(false);
}
}
else {
// Directory
string dstPath = $"{dst}/{Path.GetDirectoryName(relativeSrc)}";
Directory.CreateDirectory(dstPath);

foreach (string filename in await ListDirectory(src, cancellationToken).ConfigureAwait(false)) {
string srcFilename = $"{src}/{filename}";
string dstFilename = Path.Combine(dstPath, filename);

srcFilename = await ResolvePath(srcFilename, cancellationToken).ConfigureAwait(false);
string srcFilename = await ResolvePath($"{src}/{filename}", cancellationToken).ConfigureAwait(false);

if (await IsDir(srcFilename, cancellationToken).ConfigureAwait(false)) {
Directory.CreateDirectory(dstFilename);
Expand Down
2 changes: 1 addition & 1 deletion Netimobiledevice/Afc/CrashReportsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public CrashReportsService(LockdownServiceProvider lockdown)
_crashMoverServiceName = RSD_CRASH_MOVER_NAME;
}

_afcService = new AfcService(lockdown, _copyMobileServiceName);
_afcService = new AfcService(lockdown, _copyMobileServiceName, lockdown.Logger);
}

public void Close()
Expand Down
Loading

0 comments on commit c316de3

Please sign in to comment.