// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : Remote.cs // Author(s) : Natalia Portillo // // Component : Aaru Remote. // // --[ Description ] ---------------------------------------------------------- // // Implementation of the Aaru Remote protocol. // // --[ License ] -------------------------------------------------------------- // // This library is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation; either version 2.1 of the // License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, see . // // ---------------------------------------------------------------------------- // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ // ReSharper disable MemberCanBeInternal using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interop; using Aaru.Console; using Aaru.Decoders.ATA; using Marshal = Aaru.Helpers.Marshal; using Version = Aaru.CommonTypes.Interop.Version; namespace Aaru.Devices.Remote; /// /// Handles communication with a remote device that's connected using the AaruRemote protocol public class Remote : IDisposable { readonly string _host; readonly Socket _socket; /// Connects using TCP/IP to the specified remote /// URI of the remote /// Unsupported or invalid remote protocol. /// Host not found. /// Network error. public Remote(Uri uri) { if(uri.Scheme != "aaru" && uri.Scheme != "dic") throw new ArgumentException("Invalid remote protocol.", nameof(uri.Scheme)); _host = uri.DnsSafeHost; if(!IPAddress.TryParse(_host, out IPAddress ipAddress)) { IPHostEntry ipHostEntry = Dns.GetHostEntry(_host); ipAddress = ipHostEntry.AddressList.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork); } if(ipAddress is null) { AaruConsole.ErrorWriteLine("Host not found"); throw new SocketException(11001); } var ipEndPoint = new IPEndPoint(ipAddress, uri.Port > 0 ? uri.Port : 6666); _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _socket.Connect(ipEndPoint); AaruConsole.WriteLine("Connected to {0}", uri.Host); byte[] hdrBuf = new byte[Marshal.SizeOf()]; int len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); throw new IOException(); } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); throw new ArgumentException(); } byte[] buf; if(hdr.packetType != AaruPacketType.Hello) { if(hdr.packetType != AaruPacketType.Nop) { AaruConsole.ErrorWriteLine("Expected Hello Packet, got packet type {0}...", hdr.packetType); throw new ArgumentException(); } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); throw new IOException(); } AaruPacketNop nop = Marshal.ByteArrayToStructureLittleEndian(buf); AaruConsole.ErrorWriteLine($"{nop.reason}"); throw new ArgumentException(); } if(hdr.version != Consts.PACKET_VERSION) { AaruConsole.ErrorWriteLine("Unrecognized packet version..."); throw new ArgumentException(); } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); throw new IOException(); } AaruPacketHello serverHello = Marshal.ByteArrayToStructureLittleEndian(buf); ServerApplication = serverHello.application; ServerVersion = serverHello.version; ServerOperatingSystem = serverHello.sysname; ServerOperatingSystemVersion = serverHello.release; ServerArchitecture = serverHello.machine; ServerProtocolVersion = serverHello.maxProtocol; var clientHello = new AaruPacketHello { application = "Aaru", version = Version.GetVersion(), maxProtocol = Consts.MAX_PROTOCOL, sysname = DetectOS.GetPlatformName(DetectOS.GetRealPlatformID(), DetectOS.GetVersion()), release = DetectOS.GetVersion(), machine = RuntimeInformation.ProcessArchitecture.ToString(), hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, len = (uint)Marshal.SizeOf(), version = Consts.PACKET_VERSION, packetType = AaruPacketType.Hello } }; buf = Marshal.StructureToByteArrayLittleEndian(clientHello); len = _socket.Send(buf, SocketFlags.None); if(len >= buf.Length) return; AaruConsole.ErrorWriteLine("Could not write to the network..."); throw new IOException(); } /// Remote server application public string ServerApplication { get; } /// Remote server application version public string ServerVersion { get; } /// Remote server operating system public string ServerOperatingSystem { get; } /// Remote server operating system version public string ServerOperatingSystemVersion { get; } /// Remote server architecture public string ServerArchitecture { get; } /// Remote server protocol version public int ServerProtocolVersion { get; } /// Is remote running with administrative (aka root) privileges? public bool IsRoot { get { var cmdPkt = new AaruPacketCmdAmIRoot { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, len = (uint)Marshal.SizeOf(), version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandAmIRoot } }; byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return false; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return false; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return false; } if(hdr.packetType != AaruPacketType.ResponseAmIRoot) { AaruConsole.ErrorWriteLine("Expected Am I Root? Response Packet, got packet type {0}...", hdr.packetType); return false; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return false; } AaruPacketResAmIRoot res = Marshal.ByteArrayToStructureLittleEndian(buf); return res.am_i_root != 0; } } /// public void Dispose() => Disconnect(); /// Disconnects from remote public void Disconnect() { try { _socket.Shutdown(SocketShutdown.Both); _socket.Close(); } catch(ObjectDisposedException) { // Ignore if already disposed } } /// Lists devices attached to remote /// List of devices public DeviceInfo[] ListDevices() { var cmdPkt = new AaruPacketCommandListDevices { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, len = (uint)Marshal.SizeOf(), version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandListDevices } }; byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return Array.Empty(); } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return Array.Empty(); } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return Array.Empty(); } if(hdr.packetType != AaruPacketType.ResponseListDevices) { if(hdr.packetType != AaruPacketType.Nop) { AaruConsole.ErrorWriteLine("Expected List Devices Response Packet, got packet type {0}...", hdr.packetType); return Array.Empty(); } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return Array.Empty(); } AaruPacketNop nop = Marshal.ByteArrayToStructureLittleEndian(buf); AaruConsole.ErrorWriteLine($"{nop.reason}"); return Array.Empty(); } if(hdr.version != Consts.PACKET_VERSION) { AaruConsole.ErrorWriteLine("Unrecognized packet version..."); return Array.Empty(); } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return Array.Empty(); } AaruPacketResponseListDevices response = Marshal.ByteArrayToStructureLittleEndian(buf); List devices = new List(); int offset = Marshal.SizeOf(); int devInfoLen = Marshal.SizeOf(); for(ushort i = 0; i < response.devices; i++) { DeviceInfo dev = Marshal.ByteArrayToStructureLittleEndian(buf, offset, devInfoLen); dev.Path = dev.Path[0] == '/' ? $"aaru://{_host}{dev.Path}" : $"aaru://{_host}/{dev.Path}"; devices.Add(dev); offset += devInfoLen; } return devices.ToArray(); } /// Opens the specified device path on the remote /// Device path /// Returned error /// true if opened correctly, falseotherwise /// /// Support for the specified device has not yet been implemented in the remote /// application. /// public bool Open(string devicePath, out int lastError) { lastError = 0; var cmdPkt = new AaruPacketCommandOpenDevice { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, len = (uint)Marshal.SizeOf(), version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandOpen }, device_path = devicePath }; byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); lastError = -1; return false; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); lastError = -1; return false; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); lastError = -1; return false; } if(hdr.packetType != AaruPacketType.Nop) { AaruConsole.ErrorWriteLine("Expected List Devices Response Packet, got packet type {0}...", hdr.packetType); lastError = -1; return false; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); lastError = -1; return false; } AaruPacketNop nop = Marshal.ByteArrayToStructureLittleEndian(buf); switch(nop.reasonCode) { case AaruNopReason.OpenOk: return true; case AaruNopReason.NotImplemented: throw new NotImplementedException($"{nop.reason}"); } AaruConsole.ErrorWriteLine($"{nop.reason}"); lastError = nop.errno; return false; } /// Sends a SCSI command to the remote device /// 0 if no error occurred, otherwise, errno /// SCSI CDB /// Buffer for SCSI command response /// Buffer with the SCSI sense /// Timeout in seconds /// SCSI command transfer direction /// Time it took to execute the command in milliseconds /// /// True if SCSI command returned non-OK status and contains /// SCSI sense /// public int SendScsiCommand(byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, ScsiDirection direction, out double duration, out bool sense) { senseBuffer = null; duration = 0; sense = true; var cmdPkt = new AaruPacketCmdScsi { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandScsi }, direction = (int)direction, timeout = timeout * 1000 }; if(cdb != null) cmdPkt.cdb_len = (uint)cdb.Length; if(buffer != null) cmdPkt.buf_len = (uint)buffer.Length; cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.cdb_len + cmdPkt.buf_len); byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); byte[] buf = new byte[cmdPkt.hdr.len]; Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); if(cdb != null) Array.Copy(cdb, 0, buf, Marshal.SizeOf(), cmdPkt.cdb_len); if(buffer != null) Array.Copy(buffer, 0, buf, Marshal.SizeOf() + cmdPkt.cdb_len, cmdPkt.buf_len); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return -1; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return -1; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return -1; } if(hdr.packetType != AaruPacketType.ResponseScsi) { AaruConsole.ErrorWriteLine("Expected SCSI Response Packet, got packet type {0}...", hdr.packetType); return -1; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return -1; } AaruPacketResScsi res = Marshal.ByteArrayToStructureLittleEndian(buf); senseBuffer = new byte[res.sense_len]; Array.Copy(buf, Marshal.SizeOf(), senseBuffer, 0, res.sense_len); buffer = new byte[res.buf_len]; Array.Copy(buf, Marshal.SizeOf() + res.sense_len, buffer, 0, res.buf_len); duration = res.duration; sense = res.sense != 0; return (int)res.error_no; } /// Sends an ATA/ATAPI command to the remote device using CHS addressing /// 0 if no error occurred, otherwise, errno /// ATA registers. /// Status/error registers. /// ATA Protocol. /// Indicates which register indicates the transfer length /// Buffer for ATA/ATAPI command response /// Timeout in seconds /// /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in /// bytes. /// /// Time it took to execute the command in milliseconds /// True if ATA/ATAPI command returned non-OK status public int SendAtaCommand(AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters, AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, bool transferBlocks, out double duration, out bool sense) { duration = 0; sense = true; errorRegisters = new AtaErrorRegistersChs(); var cmdPkt = new AaruPacketCmdAtaChs { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandAtaChs }, registers = registers, protocol = (byte)protocol, transferRegister = (byte)transferRegister, transferBlocks = transferBlocks, timeout = timeout * 1000 }; if(buffer != null) cmdPkt.buf_len = (uint)buffer.Length; cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.buf_len); byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); byte[] buf = new byte[cmdPkt.hdr.len]; Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); if(buffer != null) Array.Copy(buffer, 0, buf, Marshal.SizeOf(), cmdPkt.buf_len); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return -1; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return -1; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return -1; } if(hdr.packetType != AaruPacketType.ResponseAtaChs) { AaruConsole.ErrorWriteLine("Expected ATA CHS Response Packet, got packet type {0}...", hdr.packetType); return -1; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return -1; } AaruPacketResAtaChs res = Marshal.ByteArrayToStructureLittleEndian(buf); buffer = new byte[res.buf_len]; Array.Copy(buf, Marshal.SizeOf(), buffer, 0, res.buf_len); duration = res.duration; sense = res.sense != 0; errorRegisters = res.registers; return (int)res.error_no; } /// Sends an ATA/ATAPI command to the remote device using 28-bit LBA addressing /// 0 if no error occurred, otherwise, errno /// ATA registers. /// Status/error registers. /// ATA Protocol. /// Indicates which register indicates the transfer length /// Buffer for ATA/ATAPI command response /// Timeout in seconds /// /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in /// bytes. /// /// Time it took to execute the command in milliseconds /// True if ATA/ATAPI command returned non-OK status public int SendAtaCommand(AtaRegistersLba28 registers, out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, bool transferBlocks, out double duration, out bool sense) { duration = 0; sense = true; errorRegisters = new AtaErrorRegistersLba28(); var cmdPkt = new AaruPacketCmdAtaLba28 { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandAtaLba28 }, registers = registers, protocol = (byte)protocol, transferRegister = (byte)transferRegister, transferBlocks = transferBlocks, timeout = timeout * 1000 }; if(buffer != null) cmdPkt.buf_len = (uint)buffer.Length; cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.buf_len); byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); byte[] buf = new byte[cmdPkt.hdr.len]; Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); if(buffer != null) Array.Copy(buffer, 0, buf, Marshal.SizeOf(), cmdPkt.buf_len); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return -1; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return -1; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return -1; } if(hdr.packetType != AaruPacketType.ResponseAtaLba28) { AaruConsole.ErrorWriteLine("Expected ATA LBA28 Response Packet, got packet type {0}...", hdr.packetType); return -1; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return -1; } AaruPacketResAtaLba28 res = Marshal.ByteArrayToStructureLittleEndian(buf); buffer = new byte[res.buf_len]; Array.Copy(buf, Marshal.SizeOf(), buffer, 0, res.buf_len); duration = res.duration; sense = res.sense != 0; errorRegisters = res.registers; return (int)res.error_no; } /// Sends an ATA/ATAPI command to the remote device using 48-bit LBA addressing /// 0 if no error occurred, otherwise, errno /// ATA registers. /// Status/error registers. /// ATA Protocol. /// Indicates which register indicates the transfer length /// Buffer for ATA/ATAPI command response /// Timeout in seconds /// /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in /// bytes. /// /// Time it took to execute the command in milliseconds /// True if ATA/ATAPI command returned non-OK status public int SendAtaCommand(AtaRegistersLba48 registers, out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, bool transferBlocks, out double duration, out bool sense) { duration = 0; sense = true; errorRegisters = new AtaErrorRegistersLba48(); var cmdPkt = new AaruPacketCmdAtaLba48 { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandAtaLba48 }, registers = registers, protocol = (byte)protocol, transferRegister = (byte)transferRegister, transferBlocks = transferBlocks, timeout = timeout * 1000 }; if(buffer != null) cmdPkt.buf_len = (uint)buffer.Length; cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.buf_len); byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); byte[] buf = new byte[cmdPkt.hdr.len]; Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); if(buffer != null) Array.Copy(buffer, 0, buf, Marshal.SizeOf(), cmdPkt.buf_len); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return -1; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return -1; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return -1; } if(hdr.packetType != AaruPacketType.ResponseAtaLba48) { AaruConsole.ErrorWriteLine("Expected ATA LBA48 Response Packet, got packet type {0}...", hdr.packetType); return -1; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return -1; } AaruPacketResAtaLba48 res = Marshal.ByteArrayToStructureLittleEndian(buf); buffer = new byte[res.buf_len]; Array.Copy(buf, Marshal.SizeOf(), buffer, 0, res.buf_len); duration = res.duration; sense = res.sense != 0; errorRegisters = res.registers; return (int)res.error_no; } /// Sends a MMC/SD command to the remote device /// The result of the command. /// MMC/SD opcode /// Buffer for MMC/SD command response /// Timeout in seconds /// Time it took to execute the command in milliseconds /// True if MMC/SD returned non-OK status /// True if data is sent from host to card /// True if command should be preceded with CMD55 /// Flags indicating kind and place of response /// How many blocks to transfer /// Command argument /// Response registers /// Size of block in bytes public int SendMmcCommand(MmcCommands command, bool write, bool isApplication, MmcFlags flags, uint argument, uint blockSize, uint blocks, ref byte[] buffer, out uint[] response, out double duration, out bool sense, uint timeout = 0) { duration = 0; sense = true; response = null; var cmdPkt = new AaruPacketCmdSdhci { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandSdhci }, command = new AaruCmdSdhci { command = command, write = write, application = isApplication, flags = flags, argument = argument, block_size = blockSize, blocks = blocks, timeout = timeout * 1000 } }; if(buffer != null) cmdPkt.command.buf_len = (uint)buffer.Length; cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.command.buf_len); byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); byte[] buf = new byte[cmdPkt.hdr.len]; Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); if(buffer != null) Array.Copy(buffer, 0, buf, Marshal.SizeOf(), cmdPkt.command.buf_len); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return -1; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return -1; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return -1; } if(hdr.packetType != AaruPacketType.ResponseSdhci) { AaruConsole.ErrorWriteLine("Expected SDHCI Response Packet, got packet type {0}...", hdr.packetType); return -1; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return -1; } AaruPacketResSdhci res = Marshal.ByteArrayToStructureLittleEndian(buf); buffer = new byte[res.res.buf_len]; Array.Copy(buf, Marshal.SizeOf(), buffer, 0, res.res.buf_len); duration = res.res.duration; sense = res.res.sense != 0; response = new uint[4]; response[0] = res.res.response[0]; response[1] = res.res.response[1]; response[2] = res.res.response[2]; response[3] = res.res.response[3]; return (int)res.res.error_no; } /// Gets the for the remote device /// /// /// public DeviceType GetDeviceType() { var cmdPkt = new AaruPacketCmdGetDeviceType { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, len = (uint)Marshal.SizeOf(), version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandGetType } }; byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return DeviceType.Unknown; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return DeviceType.Unknown; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return DeviceType.Unknown; } if(hdr.packetType != AaruPacketType.ResponseGetType) { AaruConsole.ErrorWriteLine("Expected Device Type Response Packet, got packet type {0}...", hdr.packetType); return DeviceType.Unknown; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return DeviceType.Unknown; } AaruPacketResGetDeviceType res = Marshal.ByteArrayToStructureLittleEndian(buf); return res.device_type; } /// Retrieves the SDHCI registers from the remote device /// CSD register /// CID register /// OCR register /// SCR register /// true if the device is attached to an SDHCI controller, false otherwise public bool GetSdhciRegisters(out byte[] csd, out byte[] cid, out byte[] ocr, out byte[] scr) { csd = null; cid = null; ocr = null; scr = null; var cmdPkt = new AaruPacketCmdGetSdhciRegisters { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, len = (uint)Marshal.SizeOf(), version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandGetSdhciRegisters } }; byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return false; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return false; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return false; } if(hdr.packetType != AaruPacketType.ResponseGetSdhciRegisters) { AaruConsole.ErrorWriteLine("Expected Device Type Response Packet, got packet type {0}...", hdr.packetType); return false; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return false; } AaruPacketResGetSdhciRegisters res = Marshal.ByteArrayToStructureLittleEndian(buf); if(res.csd_len > 0) { if(res.csd_len > 16) res.csd_len = 16; csd = new byte[res.csd_len]; Array.Copy(res.csd, 0, csd, 0, res.csd_len); } if(res.cid_len > 0) { if(res.cid_len > 16) res.cid_len = 16; cid = new byte[res.cid_len]; Array.Copy(res.cid, 0, cid, 0, res.cid_len); } if(res.ocr_len > 0) { if(res.ocr_len > 16) res.ocr_len = 16; ocr = new byte[res.ocr_len]; Array.Copy(res.ocr, 0, ocr, 0, res.ocr_len); } switch(res.scr_len) { case <= 0: return res.isSdhci; case > 16: res.scr_len = 16; break; } scr = new byte[res.scr_len]; Array.Copy(res.scr, 0, scr, 0, res.scr_len); return res.isSdhci; } /// Gets the USB data from the remote device /// USB descriptors /// USB vendor ID /// USB product ID /// USB manufacturer string /// USB product string /// USB serial number string /// true if the device is attached via USB, false otherwise public bool GetUsbData(out byte[] descriptors, out ushort idVendor, out ushort idProduct, out string manufacturer, out string product, out string serial) { descriptors = null; idVendor = 0; idProduct = 0; manufacturer = null; product = null; serial = null; var cmdPkt = new AaruPacketCmdGetUsbData { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, len = (uint)Marshal.SizeOf(), version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandGetUsbData } }; byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return false; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return false; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return false; } if(hdr.packetType != AaruPacketType.ResponseGetUsbData) { AaruConsole.ErrorWriteLine("Expected USB Data Response Packet, got packet type {0}...", hdr.packetType); return false; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return false; } AaruPacketResGetUsbData res = Marshal.ByteArrayToStructureLittleEndian(buf); if(!res.isUsb) return false; descriptors = new byte[res.descLen]; Array.Copy(res.descriptors, 0, descriptors, 0, res.descLen); idVendor = res.idVendor; idProduct = res.idProduct; manufacturer = res.manufacturer; product = res.product; serial = res.serial; return true; } /// Gets the FireWire data from the remote device /// FireWire vendor ID /// FireWire product ID /// FireWire vendor string /// FireWire model string /// FireWire GUID /// true if the device is attached via FireWire, false otherwise public bool GetFireWireData(out uint idVendor, out uint idProduct, out ulong guid, out string vendor, out string model) { idVendor = 0; idProduct = 0; guid = 0; vendor = null; model = null; var cmdPkt = new AaruPacketCmdGetFireWireData { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, len = (uint)Marshal.SizeOf(), version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandGetFireWireData } }; byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return false; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return false; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return false; } if(hdr.packetType != AaruPacketType.ResponseGetFireWireData) { AaruConsole.ErrorWriteLine("Expected FireWire Data Response Packet, got packet type {0}...", hdr.packetType); return false; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return false; } AaruPacketResGetFireWireData res = Marshal.ByteArrayToStructureLittleEndian(buf); if(!res.isFireWire) return false; idVendor = res.idVendor; idProduct = res.idModel; guid = res.guid; vendor = res.vendor; model = res.model; return true; } /// Gets the PCMCIA/CardBus data from the remote device /// Card Information Structure /// true if the device is attached via PCMCIA or CardBus, false otherwise public bool GetPcmciaData(out byte[] cis) { cis = null; var cmdPkt = new AaruPacketCmdGetPcmciaData { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, len = (uint)Marshal.SizeOf(), version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandGetPcmciaData } }; byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return false; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return false; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return false; } if(hdr.packetType != AaruPacketType.ResponseGetPcmciaData) { AaruConsole.ErrorWriteLine("Expected PCMCIA Data Response Packet, got packet type {0}...", hdr.packetType); return false; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return false; } AaruPacketResGetPcmciaData res = Marshal.ByteArrayToStructureLittleEndian(buf); if(!res.isPcmcia) return false; cis = res.cis; return true; } /// Receives data from a socket into a buffer /// Socket /// Data buffer /// Expected total size in bytes /// Socket flags /// Retrieved number of bytes static int Receive(Socket socket, byte[] buffer, int size, SocketFlags socketFlags) { int offset = 0; while(size > 0) { int got = socket.Receive(buffer, offset, size, socketFlags); if(got <= 0) break; offset += got; size -= got; } return offset; } /// Closes the remote device, without closing the network connection public void Close() { var cmdPkt = new AaruPacketCmdClose { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, len = (uint)Marshal.SizeOf(), version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandCloseDevice } }; byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); try { _socket.Send(buf, SocketFlags.None); } catch(ObjectDisposedException) { // Ignore if already disposed } } /// /// Concatenates a queue of commands to be send to a remote SecureDigital or MultiMediaCard attached to an SDHCI /// controller /// /// List of commands /// Duration to execute all commands, in milliseconds /// Set to true if any of the commands returned an error status, false otherwise /// Maximum allowed time to execute a single command /// 0 if no error occurred, otherwise, errno public int SendMultipleMmcCommands(Devices.Device.MmcSingleCommand[] commands, out double duration, out bool sense, uint timeout = 0) { if(ServerProtocolVersion < 2) return SendMultipleMmcCommandsV1(commands, out duration, out sense, timeout); sense = false; duration = 0; long packetSize = Marshal.SizeOf() + (Marshal.SizeOf() * commands.LongLength); packetSize = commands.Aggregate(packetSize, (current, command) => current + (command.buffer?.Length ?? 0)); var packet = new AaruPacketMultiCmdSdhci { cmd_count = (ulong)commands.LongLength, hdr = new AaruPacketHeader { len = (uint)packetSize, packetType = AaruPacketType.MultiCommandSdhci, remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, version = Consts.PACKET_VERSION } }; byte[] buf = new byte[packetSize]; byte[] tmp = Marshal.StructureToByteArrayLittleEndian(packet); Array.Copy(tmp, 0, buf, 0, tmp.Length); int off = tmp.Length; foreach(AaruCmdSdhci cmd in commands.Select(command => new AaruCmdSdhci { application = command.isApplication, argument = command.argument, block_size = command.blockSize, blocks = command.blocks, buf_len = (uint)(command.buffer?.Length ?? 0), command = command.command, flags = command.flags, timeout = timeout, write = command.write })) { tmp = Marshal.StructureToByteArrayLittleEndian(cmd); Array.Copy(tmp, 0, buf, off, tmp.Length); off += tmp.Length; } foreach(Devices.Device.MmcSingleCommand command in commands.Where(command => (command.buffer?.Length ?? 0) != 0)) { Array.Copy(command.buffer, 0, buf, off, command.buffer.Length); off += command.buffer.Length; } int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return -1; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return -1; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return -1; } if(hdr.packetType != AaruPacketType.ResponseMultiSdhci) { AaruConsole.ErrorWriteLine("Expected multi MMC/SD command Response Packet, got packet type {0}...", hdr.packetType); return -1; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return -1; } AaruPacketMultiCmdSdhci res = Marshal.ByteArrayToStructureLittleEndian(buf); if(res.cmd_count != (ulong)commands.Length) { AaruConsole.ErrorWriteLine("Expected the response to {0} SD/MMC commands, but got {1} responses...", commands.Length, res.cmd_count); return -1; } off = Marshal.SizeOf(); int error = 0; foreach(Devices.Device.MmcSingleCommand command in commands) { AaruResSdhci cmdRes = Marshal.ByteArrayToStructureLittleEndian(buf, off, Marshal.SizeOf()); command.response = cmdRes.response; duration += cmdRes.duration; if(cmdRes.error_no != 0 && error == 0) error = (int)cmdRes.error_no; if(cmdRes.sense != 0) sense = true; if(cmdRes.buf_len > 0) command.buffer = new byte[cmdRes.buf_len]; off += Marshal.SizeOf(); } foreach(Devices.Device.MmcSingleCommand command in commands) { Array.Copy(buf, off, command.buffer, 0, command.buffer.Length); off += command.buffer.Length; } return error; } /// /// Concatenates a queue of commands to be send to a remote SecureDigital or MultiMediaCard attached to an SDHCI /// controller, using protocol version 1 without specific support for such a queueing /// /// List of commands /// Duration to execute all commands, in milliseconds /// Set to true if any of the commands returned an error status, false otherwise /// Maximum allowed time to execute a single command /// 0 if no error occurred, otherwise, errno int SendMultipleMmcCommandsV1(Devices.Device.MmcSingleCommand[] commands, out double duration, out bool sense, uint timeout) { sense = false; duration = 0; int error = 0; foreach(Devices.Device.MmcSingleCommand command in commands) { error = SendMmcCommand(command.command, command.write, command.isApplication, command.flags, command.argument, command.blockSize, command.blocks, ref command.buffer, out command.response, out double cmdDuration, out bool cmdSense, timeout); if(cmdSense) sense = true; duration += cmdDuration; } return error; } /// Closes then immediately reopens a remote device /// Returned error number if any public bool ReOpen() { if(ServerProtocolVersion < 2) return false; var cmdPkt = new AaruPacketCmdReOpen { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, len = (uint)Marshal.SizeOf(), version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandReOpenDevice } }; byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return false; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return false; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return false; } if(hdr.packetType != AaruPacketType.Nop) { AaruConsole.ErrorWriteLine("Expected NOP Packet, got packet type {0}...", hdr.packetType); return false; } if(hdr.version != Consts.PACKET_VERSION) { AaruConsole.ErrorWriteLine("Unrecognized packet version..."); return false; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return false; } AaruPacketNop nop = Marshal.ByteArrayToStructureLittleEndian(buf); switch(nop.reasonCode) { case AaruNopReason.ReOpenOk: return true; case AaruNopReason.CloseError: case AaruNopReason.OpenError: AaruConsole.ErrorWriteLine("ReOpen error closing device..."); break; default: AaruConsole.ErrorWriteLine("ReOpen error {0} with reason: {1}...", nop.errno, nop.reason); break; } return false; } /// Reads data using operating system buffers. /// Data buffer /// Offset in remote device to start reading, in bytes /// Number of bytes to read /// Total time in milliseconds the reading took /// true if there was an error, false otherwise public bool BufferedOsRead(out byte[] buffer, long offset, uint length, out double duration) { duration = 0; buffer = null; if(ServerProtocolVersion < 2) return false; var cmdPkt = new AaruPacketCmdOsRead { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, len = (uint)Marshal.SizeOf(), version = Consts.PACKET_VERSION, packetType = AaruPacketType.CommandOsRead }, length = length, offset = (ulong)offset }; byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); int len = _socket.Send(buf, SocketFlags.None); if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); return false; } byte[] hdrBuf = new byte[Marshal.SizeOf()]; len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return false; } AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); if(hdr.remote_id != Consts.REMOTE_ID || hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); return false; } if(hdr.packetType != AaruPacketType.ResponseOsRead) { AaruConsole.ErrorWriteLine("Expected OS Read Response Packet, got packet type {0}...", hdr.packetType); return false; } if(hdr.version != Consts.PACKET_VERSION) { AaruConsole.ErrorWriteLine("Unrecognized packet version..."); return false; } buf = new byte[hdr.len]; len = Receive(_socket, buf, buf.Length, SocketFlags.None); if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); return false; } AaruPacketResOsRead osRead = Marshal.ByteArrayToStructureLittleEndian(buf); duration = osRead.duration; if(osRead.errno != 0) { AaruConsole.ErrorWriteLine("Remote error {0} in OS Read...", osRead.errno); return false; } buffer = new byte[length]; Array.Copy(buf, Marshal.SizeOf(), buffer, 0, length); return true; } }