├── .gitattributes ├── .gitignore ├── ISCSI ├── ISCSI.Client │ ├── ClientHelper.cs │ ├── ConnectionParameters.cs │ ├── ConnectionState.cs │ ├── ISCSIClient.Parameters.cs │ ├── ISCSIClient.cs │ ├── ISCSIDisk.cs │ ├── ISCSISession.cs │ └── PDUHelper.cs ├── ISCSI.Common │ ├── DefaultParameters.cs │ └── ISCSIConnectionBuffer.cs ├── ISCSI.Logging │ ├── LogEntry.cs │ └── Severity.cs ├── ISCSI.PDU │ ├── Enums │ │ ├── ISCSIOpCodeName.cs │ │ ├── ISCSIResponseName.cs │ │ ├── LoginResponseStatusClassName.cs │ │ ├── LogoutReasonCode.cs │ │ ├── LogoutResponse.cs │ │ └── RejectReason.cs │ ├── ISCSIPDU.cs │ ├── LoginRequestPDU.cs │ ├── LoginResponsePDU.cs │ ├── LogoutRequestPDU.cs │ ├── LogoutResponsePDU.cs │ ├── NOPInPDU.cs │ ├── NOPOutPDU.cs │ ├── ReadyToTransferPDU.cs │ ├── RejectPDU.cs │ ├── SCSICommandPDU.cs │ ├── SCSIDataInPDU.cs │ ├── SCSIDataOutPDU.cs │ ├── SCSIResponsePDU.cs │ ├── TextRequestPDU.cs │ └── TextResponsePDU.cs ├── ISCSI.Server │ ├── ConnectionManager.cs │ ├── ConnectionParameters.cs │ ├── ConnectionState.cs │ ├── Exceptions │ │ └── InvalidTargetTransferTagException.cs │ ├── ISCSIServer.Login.cs │ ├── ISCSIServer.Logout.cs │ ├── ISCSIServer.PDUProcessor.cs │ ├── ISCSIServer.Parameters.cs │ ├── ISCSIServer.TextRequest.cs │ ├── ISCSIServer.cs │ ├── ISCSISession.cs │ ├── ISCSITarget.cs │ ├── PDUHelper.cs │ ├── ServerResponseHelper.cs │ ├── SessionManager.cs │ ├── TargetEventArgs │ │ ├── AuthorizationRequestArgs.cs │ │ ├── SessionTerminationArgs.cs │ │ └── TextRequestArgs.cs │ ├── TargetList.cs │ └── TargetResponseHelper.cs ├── ISCSI.csproj ├── RevisionHistory.txt ├── SCSI │ ├── Enums │ │ ├── AddressingMethod.cs │ │ ├── ModePageCodeName.cs │ │ ├── PeripheralDeviceType.cs │ │ ├── SCSIOpCodeName.cs │ │ ├── SCSIStatusCodeName.cs │ │ ├── ServiceAction.cs │ │ ├── VersionDescriptorName.cs │ │ └── VitalProductDataPageName.cs │ ├── Exceptions │ │ └── UnsupportedSCSICommandException.cs │ ├── LUNStructure.cs │ ├── SCSICommandDescriptorBlock │ │ ├── InquiryCommandDescriptorBlock.cs │ │ ├── ModeSense6CommandDescriptorBlock.cs │ │ ├── SCSICommandDescriptorBlock.cs │ │ ├── SCSICommandDescriptorBlock10.cs │ │ ├── SCSICommandDescriptorBlock12.cs │ │ ├── SCSICommandDescriptorBlock16.cs │ │ └── SCSICommandDescriptorBlock6.cs │ └── SCSIReturnParameters │ │ ├── ModePages │ │ ├── CachingParametersPage.cs │ │ ├── ControlModePage.cs │ │ ├── InformationalExceptionsControlModePage.cs │ │ ├── LongLBAModeParameterBlockDescriptor.cs │ │ ├── ModePage.cs │ │ ├── ModePage0.cs │ │ ├── ModeParameterHeader10.cs │ │ ├── ModeParameterHeader6.cs │ │ ├── PowerConditionModePage.cs │ │ ├── PowerConsumptionModePage.cs │ │ ├── ShortLBAModeParameterBlockDescriptor.cs │ │ ├── SubModePage.cs │ │ └── VendorSpecificPage.cs │ │ ├── ReadCapacity10Parameter.cs │ │ ├── ReadCapacity16Parameter.cs │ │ ├── ReportLUNsParameter.cs │ │ ├── SenseDataParameter.cs │ │ ├── StandardInquiryData.cs │ │ └── VPDPages │ │ ├── BlockDeviceCharacteristicsVPDPage.cs │ │ ├── BlockLimitsVPDPage.cs │ │ ├── DeviceIdentificationVPDPage.cs │ │ ├── Enums │ │ └── IdentifierTypeName.cs │ │ ├── IdentificationDescriptor.cs │ │ ├── SupportedVitaLProductDataPages.cs │ │ └── UnitSerialNumberVPDPage.cs ├── SCSITarget │ ├── InquiryEventArgs │ │ ├── DeviceIdentificationInquiryEventArgs.cs │ │ ├── StandardInquiryEventArgs.cs │ │ └── UnitSerialNumberInquiryEventArgs.cs │ ├── SCSITarget.cs │ ├── SCSITargetInterface.cs │ └── VirtualSCSITarget.cs ├── Utilities │ ├── KeyValuePairUtils.cs │ └── SocketUtils.cs └── Win32 │ ├── SCSI │ ├── Enums │ │ └── SCSIDataDirection.cs │ └── Structures │ │ ├── SCSI_ADAPTER_BUS_INFO.cs │ │ ├── SCSI_BUS_DATA.cs │ │ ├── SCSI_INQUIRY_DATA.cs │ │ ├── SCSI_PASS_THROUGH_DIRECT.cs │ │ └── SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER.cs │ └── SCSITarget │ ├── LogicalUnitManager.cs │ ├── SCSICommandParser.cs │ └── SPTITarget.cs ├── ISCSIConsole.sln ├── ISCSIConsole ├── AddTargetForm.Designer.cs ├── AddTargetForm.cs ├── AddTargetForm.resx ├── CreateDiskImageForm.Designer.cs ├── CreateDiskImageForm.cs ├── CreateDiskImageForm.resx ├── CreateRAMDiskForm.Designer.cs ├── CreateRAMDiskForm.cs ├── CreateRAMDiskForm.resx ├── Disks │ └── VolumeDisk.cs ├── Helpers │ ├── FormattingHelper.cs │ ├── ISCSINameHelper.cs │ ├── LockUtils.cs │ ├── RuntimeHelper.cs │ └── UsageCounter.cs ├── ILMerge │ └── ILMerge.bat ├── ISCSIConsole.csproj ├── Icons │ └── SCSI.ico ├── MainForm.Designer.cs ├── MainForm.cs ├── MainForm.resx ├── Program.Log.cs ├── Program.cs ├── RevisionHistory.txt ├── SelectDiskImageForm.Designer.cs ├── SelectDiskImageForm.cs ├── SelectDiskImageForm.resx └── Win32 │ ├── SecurityHelper.cs │ ├── SelectPhysicalDiskForm.Designer.cs │ ├── SelectPhysicalDiskForm.cs │ ├── SelectPhysicalDiskForm.resx │ ├── SelectVolumeForm.Designer.cs │ ├── SelectVolumeForm.cs │ ├── SelectVolumeForm.resx │ └── VolumeInfo.cs ├── ISCSIConsole_UI.png ├── License.txt ├── Readme.md └── Utilities ├── ByteUtils ├── BigEndianReader.cs ├── BigEndianWriter.cs ├── ByteReader.cs ├── ByteUtils.cs ├── ByteWriter.cs ├── LittleEndianReader.cs └── LittleEndianWriter.cs ├── Comparers └── ReverseComparer.cs ├── Conversion ├── BigEndianConverter.cs ├── Conversion.SimpleTypes.cs └── LittleEndianConverter.cs ├── Cryptography ├── AesCcm.cs ├── AesCmac.cs └── CRC32.cs ├── Generics ├── BlockingQueue.cs ├── KeyValuePairList.Sort.cs ├── KeyValuePairList.cs ├── Map.cs ├── Reference.cs └── SortedList.cs ├── Strings └── QuotedStringUtils.cs ├── Threading ├── CountdownLatch.cs └── Parallel.cs └── Utilities.csproj /.gitattributes: -------------------------------------------------------------------------------- 1 | # Unsetting the text attribute on a path tells Git not to attempt any end-of-line conversion upon checkin or checkout 2 | * -text 3 | 4 | # Custom for Visual Studio 5 | *.cs -text diff=csharp 6 | *.csproj -text merge=union 7 | *.sln -text merge=union eol=crlf 8 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Client/ConnectionParameters.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace ISCSI.Client 12 | { 13 | internal class ConnectionParameters 14 | { 15 | public ISCSISession Session; 16 | public ushort CID; // connection ID, generated by the initiator 17 | 18 | public int InitiatorMaxRecvDataSegmentLength = ISCSIClient.DeclaredParameters.MaxRecvDataSegmentLength; 19 | public int TargetMaxRecvDataSegmentLength = DefaultParameters.Connection.MaxRecvDataSegmentLength; 20 | 21 | public bool StatusNumberingStarted; 22 | public uint ExpStatSN; 23 | 24 | public string ConnectionIdentifier 25 | { 26 | get 27 | { 28 | return String.Format("ISID={0},TSIH={1},CID={2}", Session == null ? "0" : Session.ISID.ToString("x"), Session == null ? "0" : Session.TSIH.ToString("x"), this.CID.ToString("x")); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Client/ConnectionState.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace ISCSI.Client 12 | { 13 | internal class ConnectionState 14 | { 15 | public static int ReceiveBufferSize = ISCSIPDU.BasicHeaderSegmentLength + ISCSIClient.DeclaredParameters.MaxRecvDataSegmentLength; 16 | public ISCSIConnectionReceiveBuffer ReceiveBuffer = new ISCSIConnectionReceiveBuffer(ReceiveBufferSize); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Client/ISCSIClient.Parameters.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | 9 | namespace ISCSI.Client 10 | { 11 | public partial class ISCSIClient 12 | { 13 | public class DesiredParameters 14 | { 15 | // Session parameters that will be offered to the target: 16 | public static bool InitialR2T = true; 17 | public static bool ImmediateData = true; 18 | public static int MaxBurstLength = DefaultParameters.Session.MaxBurstLength; 19 | public static int FirstBurstLength = DefaultParameters.Session.FirstBurstLength; 20 | public static int DefaultTime2Wait = 0; 21 | public static int DefaultTime2Retain = 20; 22 | public static int MaxOutstandingR2T = 1; 23 | public static bool DataPDUInOrder = true; 24 | public static bool DataSequenceInOrder = true; 25 | public static int ErrorRecoveryLevel = 0; 26 | public static int MaxConnections = 1; 27 | } 28 | 29 | public class DeclaredParameters 30 | { 31 | // Connection parameters: 32 | public static int MaxRecvDataSegmentLength = 262144; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Client/ISCSIDisk.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using DiskAccessLibrary; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Net; 11 | 12 | namespace ISCSI.Client 13 | { 14 | /// 15 | /// Disk Adapter utilizing ISCSIClient 16 | /// 17 | public class ISCSIDisk : Disk 18 | { 19 | private ISCSIClient m_client; 20 | private int m_bytesPerSector; 21 | private long m_size; 22 | private bool m_isLoggedIn; 23 | private ushort m_lun; 24 | 25 | public ISCSIDisk() 26 | { 27 | string initiatorName = "iqn.1991-05.com.microsoft:" + Environment.MachineName; 28 | m_client = new ISCSIClient(initiatorName); 29 | } 30 | 31 | public ISCSIDisk(string initiatorName) 32 | { 33 | m_client = new ISCSIClient(initiatorName); 34 | } 35 | 36 | public bool Connect(IPAddress targetAddress, int targetPort, string targetName, ushort lun) 37 | { 38 | bool isConnected = m_client.Connect(targetAddress, targetPort); 39 | if (isConnected) 40 | { 41 | m_isLoggedIn = m_client.Login(targetName); 42 | if (m_isLoggedIn) 43 | { 44 | List luns = m_client.GetLUNList(); 45 | if (luns.Contains(lun)) 46 | { 47 | m_lun = lun; 48 | m_size = (long)m_client.ReadCapacity(lun, out m_bytesPerSector); 49 | return true; 50 | } 51 | m_client.Logout(); 52 | m_isLoggedIn = false; 53 | } 54 | } 55 | return false; 56 | } 57 | 58 | public void Disconnect() 59 | { 60 | if (m_isLoggedIn) 61 | { 62 | m_client.Logout(); 63 | } 64 | m_client.Disconnect(); 65 | } 66 | 67 | public override byte[] ReadSectors(long sectorIndex, int sectorCount) 68 | { 69 | if (!m_isLoggedIn) 70 | { 71 | throw new InvalidOperationException("Not connected"); 72 | } 73 | return m_client.Read(m_lun, (ulong)sectorIndex, (uint)sectorCount, m_bytesPerSector); 74 | } 75 | 76 | public override void WriteSectors(long sectorIndex, byte[] data) 77 | { 78 | if (!m_isLoggedIn) 79 | { 80 | throw new InvalidOperationException("Not connected"); 81 | } 82 | if (IsReadOnly) 83 | { 84 | throw new UnauthorizedAccessException("Attempted to perform write on a readonly disk"); 85 | } 86 | 87 | bool success = m_client.Write(m_lun, (ulong)sectorIndex, data, m_bytesPerSector); 88 | if (!success) 89 | { 90 | string message = String.Format("Failed to write to sector {0}", sectorIndex); 91 | } 92 | } 93 | 94 | public override int BytesPerSector 95 | { 96 | get 97 | { 98 | return m_bytesPerSector; 99 | } 100 | } 101 | 102 | public override long Size 103 | { 104 | get 105 | { 106 | return m_size; 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Client/ISCSISession.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace ISCSI.Client 12 | { 13 | internal class ISCSISession 14 | { 15 | public ulong ISID; // Initiator Session ID 16 | public ushort TSIH; // Target Session Identifying Handle 17 | 18 | public int MaxConnections = DefaultParameters.Session.MaxConnections; 19 | public bool InitialR2T = DefaultParameters.Session.InitialR2T; 20 | public bool ImmediateData = DefaultParameters.Session.ImmediateData; 21 | public int MaxBurstLength = DefaultParameters.Session.MaxBurstLength; 22 | public int FirstBurstLength = DefaultParameters.Session.FirstBurstLength; 23 | public int DefaultTime2Wait = DefaultParameters.Session.DefaultTime2Wait; 24 | public int DefaultTime2Retain = DefaultParameters.Session.DefaultTime2Retain; 25 | public int MaxOutstandingR2T = DefaultParameters.Session.MaxOutstandingR2T; 26 | public bool DataPDUInOrder = DefaultParameters.Session.DataPDUInOrder; 27 | public bool DataSequenceInOrder = DefaultParameters.Session.DataSequenceInOrder; 28 | public int ErrorRecoveryLevel = DefaultParameters.Session.ErrorRecoveryLevel; 29 | 30 | private ushort m_nextCID = 1; 31 | private uint m_nextTaskTag = 1; 32 | private uint m_nextCmdSN = 1; 33 | 34 | public ushort GetNextCID() 35 | { 36 | ushort cid = m_nextCID; 37 | m_nextCID++; 38 | return cid; 39 | } 40 | 41 | /// 42 | /// Allocate Initiator Task Tag 43 | /// 44 | public uint GetNextTaskTag() 45 | { 46 | uint taskTag = m_nextTaskTag; 47 | m_nextTaskTag++; 48 | return taskTag; 49 | } 50 | 51 | // CmdSN does not advance after a command marked for immediate delivery is sent 52 | public uint GetNextCmdSN(bool increment) 53 | { 54 | uint cmdSN = m_nextCmdSN; 55 | if (increment) 56 | { 57 | m_nextCmdSN++; 58 | } 59 | return cmdSN; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Client/PDUHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace ISCSI.Client 12 | { 13 | public class PDUHelper 14 | { 15 | public static uint? GetStatSN(ISCSIPDU pdu) 16 | { 17 | if (pdu is NOPInPDU) 18 | { 19 | return ((NOPInPDU)pdu).StatSN; 20 | } 21 | else if (pdu is SCSIResponsePDU) 22 | { 23 | return ((SCSIResponsePDU)pdu).StatSN; 24 | } 25 | else if (pdu is LoginResponsePDU) 26 | { 27 | return ((LoginResponsePDU)pdu).StatSN; 28 | } 29 | else if (pdu is TextResponsePDU) 30 | { 31 | return ((TextResponsePDU)pdu).StatSN; 32 | } 33 | else if (pdu is SCSIDataInPDU && ((SCSIDataInPDU)pdu).StatusPresent) // RFC 3720: StatSN [..] only have meaningful content if the S bit is set to 1 34 | { 35 | return ((SCSIDataInPDU)pdu).StatSN; 36 | } 37 | else if (pdu is LogoutResponsePDU) 38 | { 39 | return ((LogoutResponsePDU)pdu).StatSN; 40 | } 41 | else if (pdu is ReadyToTransferPDU) 42 | { 43 | return ((ReadyToTransferPDU)pdu).StatSN; 44 | } 45 | else if (pdu is RejectPDU) 46 | { 47 | return ((RejectPDU)pdu).StatSN; 48 | } 49 | return null; 50 | } 51 | 52 | public static void SetExpStatSN(ISCSIPDU pdu, uint expStatSN) 53 | { 54 | if (pdu is NOPOutPDU) 55 | { 56 | ((NOPOutPDU)pdu).ExpStatSN = expStatSN; 57 | } 58 | else if (pdu is SCSICommandPDU) 59 | { 60 | ((SCSICommandPDU)pdu).ExpStatSN = expStatSN; 61 | } 62 | else if (pdu is LoginRequestPDU) 63 | { 64 | ((LoginRequestPDU)pdu).ExpStatSN = expStatSN; 65 | } 66 | else if (pdu is TextRequestPDU) 67 | { 68 | ((TextRequestPDU)pdu).ExpStatSN = expStatSN; 69 | } 70 | else if (pdu is SCSIDataOutPDU) 71 | { 72 | ((SCSIDataOutPDU)pdu).ExpStatSN = expStatSN; 73 | } 74 | else if (pdu is LogoutRequestPDU) 75 | { 76 | ((LogoutRequestPDU)pdu).ExpStatSN = expStatSN; 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Common/DefaultParameters.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | 8 | namespace ISCSI 9 | { 10 | /// 11 | /// Default operational parameters for iSCSI session / connection, as specified in RFC 3720. 12 | /// 13 | public class DefaultParameters 14 | { 15 | public class Session 16 | { 17 | /// 18 | /// The maximum number of connections per session. 19 | /// 20 | public const int MaxConnections = 1; 21 | 22 | /// 23 | /// Allow the initiator to start sending data to a target as if it has received an initial R2T 24 | /// 25 | public const bool InitialR2T = true; 26 | 27 | public const bool ImmediateData = true; 28 | 29 | /// 30 | /// The total of all the DataSegmentLength of all PDUs in a sequence MUST not exceed MaxBurstLength. 31 | /// Maximum SCSI data payload in bytes in a Data-In or a solicited Data-Out iSCSI sequence (i.e. that belongs to a single command). 32 | /// Irrelevant to the target in general, the initiator instructs us using ExpectedDataTransferLength. 33 | /// 34 | public const int MaxBurstLength = 262144; 35 | 36 | /// 37 | /// The total of all the DataSegmentLength of all PDUs in a sequence MUST not exceed FirstBurstLength for unsolicited data. 38 | /// Maximum amount in bytes of unsolicited [SCSI] data an iSCSI initiator may send to the target during the execution of a single SCSI command. 39 | /// Irrelevant to the target in general, irrelevant when (InitialR2T = Yes and) ImmediateData = No. 40 | /// 41 | public const int FirstBurstLength = 65536; 42 | 43 | /// 44 | /// Minimum time, in seconds, to wait before attempting an explicit/implicit logout after connection termination / reset. 45 | /// 46 | public const int DefaultTime2Wait = 2; 47 | 48 | /// 49 | /// Maximum time, in seconds after an initial wait (Time2Wait), before which an active task reassignment 50 | /// is still possible after an unexpected connection termination or a connection reset. 51 | /// 52 | public const int DefaultTime2Retain = 20; 53 | 54 | public const int MaxOutstandingR2T = 1; 55 | 56 | public const bool DataPDUInOrder = true; 57 | 58 | public const bool DataSequenceInOrder = true; 59 | 60 | public const int ErrorRecoveryLevel = 0; 61 | } 62 | 63 | public class Connection 64 | { 65 | /// 66 | /// Maximum data segment length that the target or initator can receive in an iSCSI PDU. 67 | /// Per direction parameter that the target or initator declares. 68 | /// The default MaxRecvDataSegmentLength is used during Login. 69 | /// 70 | public const int MaxRecvDataSegmentLength = 8192; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Logging/LogEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2024 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | 9 | namespace ISCSI.Logging 10 | { 11 | public class LogEntry : EventArgs 12 | { 13 | public DateTime Time; 14 | public Severity Severity; 15 | public string Source; 16 | public string Message; 17 | 18 | public LogEntry(DateTime time, Severity severity, string source, string message) 19 | { 20 | Time = time; 21 | Severity = severity; 22 | Source = source; 23 | Message = message; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Logging/Severity.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2024 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | namespace ISCSI.Logging 8 | { 9 | public enum Severity 10 | { 11 | Critical = 1, 12 | Error = 2, 13 | Warning = 3, 14 | Information = 4, 15 | Verbose = 5, 16 | Debug = 6, 17 | Trace = 7, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/Enums/ISCSIOpCodeName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace ISCSI 3 | { 4 | public enum ISCSIOpCodeName : byte 5 | { 6 | NOPOut = 0x00, 7 | SCSICommand = 0x01, 8 | //SCSITaskManagementFunctionRequest = 0x02, 9 | LoginRequest = 0x03, 10 | TextRequest = 0x04, 11 | SCSIDataOut = 0x05, 12 | LogoutRequest = 0x06, 13 | NOPIn = 0x20, 14 | SCSIResponse = 0x21, 15 | //SCSITaskManagementFunctionResponse = 0x22, 16 | LoginResponse = 0x23, 17 | TextResponse = 0x24, 18 | SCSIDataIn = 0x25, 19 | LogoutResponse = 0x26, 20 | ReadyToTransfer = 0x31, 21 | //AsynchronousMessage = 0x32, 22 | Reject = 0x3F, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/Enums/ISCSIResponseName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace ISCSI 3 | { 4 | public enum ISCSIResponseName : byte 5 | { 6 | CommandCompletedAtTarget = 0x00, 7 | TargetFailure = 0x01, 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/Enums/LoginResponseStatusClassName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace ISCSI 3 | { 4 | public enum LoginResponseStatusName : ushort 5 | { 6 | Success = 0x00, 7 | TargetMovedTemporarily = 0x101, 8 | TargetMovedPermanently = 0x102, 9 | InitiatorError = 0x200, 10 | AuthenticationFailure = 0x201, 11 | AuthorizationFailure = 0x202, 12 | NotFound = 0x203, 13 | TargetRemoved = 0x204, 14 | UnsupportedVersion = 0x205, 15 | TooManyConnections = 0x206, 16 | MissingParameter = 0x207, 17 | CanNotIncludeInSession = 0x208, 18 | SessionTypeNotSupported = 0x209, 19 | SessionDoesNotExist = 0x20a, 20 | InvalidDuringLogon = 0x20b, 21 | TargetError = 0x300, 22 | ServiceUnavailable = 0x301, 23 | OutOfResources = 0x302, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/Enums/LogoutReasonCode.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace ISCSI 3 | { 4 | public enum LogoutReasonCode : byte 5 | { 6 | CloseTheSession = 0, 7 | CloseTheConnection = 1, 8 | RemoveTheConnectionForRecovery = 2, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/Enums/LogoutResponse.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace ISCSI 3 | { 4 | public enum LogoutResponse 5 | { 6 | ClosedSuccessfully = 0, 7 | CIDNotFound = 1, 8 | ConnectionRecoveryNotSupported = 2, 9 | CleanupFailed = 3, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/Enums/RejectReason.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace ISCSI 3 | { 4 | public enum RejectReason : byte 5 | { 6 | Reserved = 0x01, 7 | DataDigestError = 0x02, 8 | SnackReject = 0x03, 9 | ProtocolError = 0x04, 10 | CommandNotSupported = 0x05, 11 | ImmediateCommandReject = 0x06, // too many immediate commands 12 | TaskInProgress = 0x07, 13 | InvalidDataAck = 0x08, 14 | InvalidPDUField = 0x09, 15 | LongOperationReject = 0x0A, // Can't generate Target Transfer Tag - out of resources 16 | NegotiationReset = 0x0B, 17 | WaitingforLogout = 0x0C, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/LoginRequestPDU.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace ISCSI 13 | { 14 | public class LoginRequestPDU : ISCSIPDU 15 | { 16 | public bool Transit; // indicates that the initiator is ready to transit to the next stage 17 | public bool Continue; // indicates that the text (set of key=value pairs) in this Login Request is not complete 18 | public byte CurrentStage; // 0..3 19 | public byte NextStage; // 0..3 20 | 21 | public byte VersionMax; 22 | public byte VersionMin; 23 | 24 | public ulong ISID; 25 | public ushort TSIH; 26 | public ushort CID; // ConnectionID 27 | public uint CmdSN; 28 | public uint ExpStatSN; 29 | 30 | public string LoginParametersText = String.Empty; // A key=value pair can start in one PDU and continue on the next 31 | 32 | public LoginRequestPDU() : base() 33 | { 34 | OpCode = ISCSIOpCodeName.LoginRequest; 35 | ImmediateDelivery = true; 36 | } 37 | 38 | public LoginRequestPDU(byte[] buffer, int offset) : base(buffer, offset) 39 | { 40 | Transit = Final; // the Transit bit replaces the Final bit 41 | Continue = (OpCodeSpecificHeader[0] & 0x40) != 0; 42 | CurrentStage = (byte)((OpCodeSpecificHeader[0] & 0x0C) >> 2); 43 | NextStage = (byte)(OpCodeSpecificHeader[0] & 0x03); 44 | 45 | VersionMax = OpCodeSpecificHeader[1]; 46 | VersionMin = OpCodeSpecificHeader[2]; 47 | 48 | ISID = (ulong)BigEndianConverter.ToUInt32(LUNOrOpCodeSpecific, 0) << 16 | BigEndianConverter.ToUInt16(LUNOrOpCodeSpecific, 4); 49 | TSIH = BigEndianConverter.ToUInt16(LUNOrOpCodeSpecific, 6); 50 | 51 | CID = BigEndianConverter.ToUInt16(OpCodeSpecific, 0); 52 | CmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4); 53 | ExpStatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8); 54 | 55 | LoginParametersText = Encoding.ASCII.GetString(Data); 56 | } 57 | 58 | public override byte[] GetBytes() 59 | { 60 | if (Transit) 61 | { 62 | Final = true; // the Transit bit replaces the Final bit 63 | } 64 | if (Continue) 65 | { 66 | OpCodeSpecificHeader[0] |= 0x40; 67 | } 68 | OpCodeSpecificHeader[0] |= (byte)(CurrentStage << 2); 69 | OpCodeSpecificHeader[0] |= NextStage; 70 | 71 | OpCodeSpecificHeader[1] = VersionMax; 72 | OpCodeSpecificHeader[2] = VersionMin; 73 | 74 | BigEndianWriter.WriteUInt64(LUNOrOpCodeSpecific, 0, ISID << 16 | TSIH); 75 | 76 | BigEndianWriter.WriteUInt16(OpCodeSpecific, 0, CID); 77 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 4, CmdSN); 78 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 8, ExpStatSN); 79 | 80 | Data = ASCIIEncoding.ASCII.GetBytes(LoginParametersText); 81 | 82 | return base.GetBytes(); 83 | } 84 | 85 | public List> LoginParameters 86 | { 87 | set 88 | { 89 | LoginParametersText = KeyValuePairUtils.ToNullDelimitedString(value); 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/LoginResponsePDU.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace ISCSI 13 | { 14 | public class LoginResponsePDU : ISCSIPDU 15 | { 16 | public bool Transit; 17 | public bool Continue; 18 | public byte CurrentStage; // 0..3 19 | public byte NextStage; // 0..3 20 | 21 | public byte VersionMax; 22 | public byte VersionActive; 23 | public ulong ISID; 24 | public ushort TSIH; 25 | 26 | public uint StatSN; 27 | public uint ExpCmdSN; 28 | public uint MaxCmdSN; 29 | public LoginResponseStatusName Status; // StatusClass & StatusDetail 30 | public string LoginParametersText = String.Empty; // A key=value pair can start in one PDU and continue on the next 31 | 32 | public LoginResponsePDU() : base() 33 | { 34 | OpCode = ISCSIOpCodeName.LoginResponse; 35 | } 36 | 37 | public LoginResponsePDU(byte[] buffer, int offset) : base(buffer, offset) 38 | { 39 | Transit = Final; // the Transit bit replaces the Final bit 40 | Continue = (OpCodeSpecificHeader[0] & 0x40) != 0; 41 | CurrentStage = (byte)((OpCodeSpecificHeader[0] & 0x0C) >> 2); 42 | NextStage = (byte)(OpCodeSpecificHeader[0] & 0x03); 43 | 44 | VersionMax = OpCodeSpecificHeader[1]; 45 | VersionActive = OpCodeSpecificHeader[2]; 46 | ISID = (ulong)BigEndianConverter.ToUInt32(LUNOrOpCodeSpecific, 0) << 16 | BigEndianConverter.ToUInt16(LUNOrOpCodeSpecific, 4); 47 | TSIH = BigEndianConverter.ToUInt16(LUNOrOpCodeSpecific, 6); 48 | 49 | StatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4); 50 | ExpCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8); 51 | MaxCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 12); 52 | Status = (LoginResponseStatusName)BigEndianConverter.ToUInt16(OpCodeSpecific, 16); 53 | 54 | string parametersString = Encoding.ASCII.GetString(Data); 55 | LoginParameters = KeyValuePairUtils.GetKeyValuePairList(parametersString); 56 | } 57 | 58 | public override byte[] GetBytes() 59 | { 60 | if (Transit) 61 | { 62 | Final = true; // the Transit bit replaces the Final bit 63 | } 64 | if (Continue) 65 | { 66 | OpCodeSpecificHeader[0] |= 0x40; 67 | } 68 | OpCodeSpecificHeader[0] |= (byte)(CurrentStage << 2); 69 | OpCodeSpecificHeader[0] |= (byte)NextStage; 70 | 71 | OpCodeSpecificHeader[1] = VersionMax; 72 | OpCodeSpecificHeader[2] = VersionActive; 73 | BigEndianWriter.WriteUInt64(LUNOrOpCodeSpecific, 0, ISID << 16 | TSIH); 74 | 75 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 4, StatSN); 76 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 8, ExpCmdSN); 77 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 12, MaxCmdSN); 78 | BigEndianWriter.WriteUInt16(OpCodeSpecific, 16, (ushort)Status); 79 | 80 | Data = ASCIIEncoding.ASCII.GetBytes(LoginParametersText); 81 | 82 | return base.GetBytes(); 83 | } 84 | 85 | public List> LoginParameters 86 | { 87 | set 88 | { 89 | LoginParametersText = KeyValuePairUtils.ToNullDelimitedString(value); 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/LogoutRequestPDU.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace ISCSI 13 | { 14 | public class LogoutRequestPDU : ISCSIPDU 15 | { 16 | public LogoutReasonCode ReasonCode; 17 | 18 | public ushort CID; 19 | public uint CmdSN; 20 | public uint ExpStatSN; 21 | 22 | public LogoutRequestPDU() : base() 23 | { 24 | OpCode = ISCSIOpCodeName.LogoutRequest; 25 | Final = true; 26 | } 27 | 28 | public LogoutRequestPDU(byte[] buffer, int offset) : base(buffer, offset) 29 | { 30 | ReasonCode = (LogoutReasonCode)(OpCodeSpecificHeader[0] & 0x7F); 31 | CID = BigEndianConverter.ToUInt16(OpCodeSpecific, 0); 32 | CmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4); 33 | ExpStatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8); 34 | } 35 | 36 | public override byte[] GetBytes() 37 | { 38 | OpCodeSpecificHeader[0] = (byte)ReasonCode; // Final bit will be added by base.GetBytes() 39 | 40 | BigEndianWriter.WriteUInt16(OpCodeSpecific, 0, CID); 41 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 4, CmdSN); 42 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 8, ExpStatSN); 43 | 44 | return base.GetBytes(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/LogoutResponsePDU.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace ISCSI 13 | { 14 | public class LogoutResponsePDU : ISCSIPDU 15 | { 16 | public LogoutResponse Response; 17 | 18 | public uint StatSN; 19 | public uint ExpCmdSN; 20 | public uint MaxCmdSN; 21 | public ushort TimeToWait; 22 | public ushort TimeToRetain; 23 | 24 | public LogoutResponsePDU() : base() 25 | { 26 | OpCode = ISCSIOpCodeName.LogoutResponse; 27 | Final = true; 28 | } 29 | 30 | public LogoutResponsePDU(byte[] buffer, int offset) : base(buffer, offset) 31 | { 32 | Response = (LogoutResponse)OpCodeSpecificHeader[1]; 33 | StatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4); 34 | ExpCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8); 35 | MaxCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 12); 36 | 37 | TimeToWait = BigEndianConverter.ToUInt16(OpCodeSpecific, 20); 38 | TimeToRetain = BigEndianConverter.ToUInt16(OpCodeSpecific, 22); 39 | } 40 | 41 | public override byte[] GetBytes() 42 | { 43 | OpCodeSpecificHeader[1] = (byte)Response; 44 | 45 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 4, StatSN); 46 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 8, ExpCmdSN); 47 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 12, MaxCmdSN); 48 | BigEndianWriter.WriteUInt16(OpCodeSpecific, 20, TimeToWait); 49 | BigEndianWriter.WriteUInt16(OpCodeSpecific, 22, TimeToRetain); 50 | 51 | return base.GetBytes(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/NOPInPDU.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using SCSI; 11 | using Utilities; 12 | 13 | namespace ISCSI 14 | { 15 | // NOP-Out = Sent back from the target to the initiator (in response to a NOP-In PDU) 16 | public class NOPInPDU : ISCSIPDU 17 | { 18 | public LUNStructure LUN; 19 | public uint TargetTransferTag; 20 | public uint StatSN; 21 | public uint ExpCmdSN; 22 | public uint MaxCmdSN; 23 | 24 | public NOPInPDU() 25 | { 26 | OpCode = ISCSIOpCodeName.NOPIn; 27 | Final = true; 28 | } 29 | 30 | public NOPInPDU(byte[] buffer, int offset) : base(buffer, offset) 31 | { 32 | LUN = new LUNStructure(LUNOrOpCodeSpecific, 0); 33 | 34 | TargetTransferTag = BigEndianConverter.ToUInt32(OpCodeSpecific, 0); 35 | StatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4); 36 | ExpCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8); 37 | MaxCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 12); 38 | } 39 | 40 | public override byte[] GetBytes() 41 | { 42 | LUNOrOpCodeSpecific = LUN.GetBytes(); 43 | 44 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 0, TargetTransferTag); 45 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 4, StatSN); 46 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 8, ExpCmdSN); 47 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 12, MaxCmdSN); 48 | 49 | return base.GetBytes(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/NOPOutPDU.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using SCSI; 11 | using Utilities; 12 | 13 | namespace ISCSI 14 | { 15 | // NOP-Out = Sent from the initiator to the target 16 | public class NOPOutPDU : ISCSIPDU 17 | { 18 | public LUNStructure LUN; 19 | public uint TargetTransferTag; 20 | public uint CmdSN; 21 | public uint ExpStatSN; 22 | 23 | public NOPOutPDU() 24 | { 25 | OpCode = ISCSIOpCodeName.NOPOut; 26 | Final = true; 27 | } 28 | 29 | public NOPOutPDU(byte[] buffer, int offset) : base(buffer, offset) 30 | { 31 | LUN = new LUNStructure(LUNOrOpCodeSpecific, 0); 32 | 33 | TargetTransferTag = BigEndianConverter.ToUInt32(OpCodeSpecific, 0); 34 | CmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4); 35 | ExpStatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8); 36 | } 37 | 38 | public override byte[] GetBytes() 39 | { 40 | LUNOrOpCodeSpecific = LUN.GetBytes(); 41 | 42 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 0, TargetTransferTag); 43 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 4, CmdSN); 44 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 8, ExpStatSN); 45 | 46 | return base.GetBytes(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/ReadyToTransferPDU.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using SCSI; 11 | using Utilities; 12 | 13 | namespace ISCSI 14 | { 15 | public class ReadyToTransferPDU : ISCSIPDU 16 | { 17 | public LUNStructure LUN; 18 | public uint TargetTransferTag; 19 | public uint StatSN; 20 | public uint ExpCmdSN; 21 | public uint MaxCmdSN; 22 | public uint R2TSN; 23 | public uint BufferOffset; 24 | public uint DesiredDataTransferLength; 25 | 26 | public ReadyToTransferPDU() 27 | { 28 | OpCode = ISCSIOpCodeName.ReadyToTransfer; 29 | Final = true; 30 | } 31 | 32 | public ReadyToTransferPDU(byte[] buffer, int offset) : base(buffer, offset) 33 | { 34 | LUN = new LUNStructure(LUNOrOpCodeSpecific, 0); 35 | TargetTransferTag = BigEndianConverter.ToUInt32(OpCodeSpecific, 0); 36 | StatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4); 37 | ExpCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8); 38 | MaxCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 12); 39 | R2TSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 16); 40 | BufferOffset = BigEndianConverter.ToUInt32(OpCodeSpecific, 20); 41 | DesiredDataTransferLength = BigEndianConverter.ToUInt32(OpCodeSpecific, 24); 42 | } 43 | 44 | public override byte[] GetBytes() 45 | { 46 | LUNOrOpCodeSpecific = LUN.GetBytes(); 47 | 48 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 0, TargetTransferTag); 49 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 4, StatSN); 50 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 8, ExpCmdSN); 51 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 12, MaxCmdSN); 52 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 16, R2TSN); 53 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 20, BufferOffset); 54 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 24, DesiredDataTransferLength); 55 | 56 | return base.GetBytes(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/RejectPDU.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace ISCSI 13 | { 14 | public class RejectPDU : ISCSIPDU 15 | { 16 | public RejectReason Reason; 17 | public uint StatSN; 18 | public uint ExpCmdSN; 19 | public uint MaxCmdSN; 20 | public uint DataSN_R2TSN; 21 | 22 | public RejectPDU() : base() 23 | { 24 | OpCode = ISCSIOpCodeName.Reject; 25 | Final = true; 26 | InitiatorTaskTag = 0xFFFFFFFF; 27 | } 28 | 29 | public RejectPDU(byte[] buffer, int offset) : base(buffer, offset) 30 | { 31 | Reason = (RejectReason)OpCodeSpecificHeader[1]; 32 | StatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4); 33 | ExpCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8); 34 | MaxCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 12); 35 | DataSN_R2TSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 16); 36 | } 37 | 38 | public override byte[] GetBytes() 39 | { 40 | OpCodeSpecificHeader[1] = (byte)Reason; 41 | 42 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 4, StatSN); 43 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 8, ExpCmdSN); 44 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 12, MaxCmdSN); 45 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 16, DataSN_R2TSN); 46 | 47 | return base.GetBytes(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/SCSICommandPDU.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using SCSI; 11 | using Utilities; 12 | 13 | namespace ISCSI 14 | { 15 | public class SCSICommandPDU : ISCSIPDU 16 | { 17 | public bool Read; 18 | public bool Write; 19 | public byte TaskAttributes; 20 | public LUNStructure LUN; 21 | public uint ExpectedDataTransferLength; // in bytes (for the whole operation and not just this command) 22 | public uint CmdSN; 23 | public uint ExpStatSN; 24 | public byte[] CommandDescriptorBlock; 25 | 26 | public SCSICommandPDU() : base() 27 | { 28 | OpCode = ISCSIOpCodeName.SCSICommand; 29 | } 30 | 31 | public SCSICommandPDU(byte[] buffer, int offset) : base(buffer, offset) 32 | { 33 | Read = (OpCodeSpecificHeader[0] & 0x40) != 0; 34 | Write = (OpCodeSpecificHeader[0] & 0x20) != 0; 35 | TaskAttributes = (byte)(OpCodeSpecificHeader[0] & 0x7); 36 | 37 | LUN = new LUNStructure(LUNOrOpCodeSpecific, 0); 38 | 39 | ExpectedDataTransferLength = BigEndianConverter.ToUInt32(OpCodeSpecific, 0); 40 | CmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4); 41 | ExpStatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8); 42 | 43 | CommandDescriptorBlock = ByteReader.ReadBytes(OpCodeSpecific, 12, 16); 44 | } 45 | 46 | public override byte[] GetBytes() 47 | { 48 | if (Read) 49 | { 50 | OpCodeSpecificHeader[0] |= 0x40; 51 | } 52 | if (Write) 53 | { 54 | OpCodeSpecificHeader[0] |= 0x20; 55 | } 56 | OpCodeSpecificHeader[0] |= TaskAttributes; 57 | 58 | LUNOrOpCodeSpecific = LUN.GetBytes(); 59 | 60 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 0, ExpectedDataTransferLength); 61 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 4, CmdSN); 62 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 8, ExpStatSN); 63 | ByteWriter.WriteBytes(OpCodeSpecific, 12, CommandDescriptorBlock); 64 | 65 | return base.GetBytes(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/SCSIDataInPDU.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using SCSI; 11 | using Utilities; 12 | 13 | namespace ISCSI 14 | { 15 | // Data-In = Data sent to the initiator (READ operations) 16 | public class SCSIDataInPDU : ISCSIPDU 17 | { 18 | public bool Acknowledge; 19 | public bool ResidualOverflow; 20 | public bool ResidualUnderflow; 21 | public bool StatusPresent; // indicate that the Command Status field contains status 22 | public SCSIStatusCodeName Status; 23 | public LUNStructure LUN; 24 | public uint TargetTransferTag; 25 | public uint StatSN; 26 | public uint ExpCmdSN; 27 | public uint MaxCmdSN; 28 | public uint DataSN; 29 | public uint BufferOffset; 30 | public uint ResidualCount; 31 | 32 | public SCSIDataInPDU() 33 | { 34 | OpCode = ISCSIOpCodeName.SCSIDataIn; 35 | } 36 | 37 | public SCSIDataInPDU(byte[] buffer, int offset) : base(buffer, offset) 38 | { 39 | Acknowledge = (OpCodeSpecificHeader[0] & 0x40) != 0; 40 | ResidualOverflow = (OpCodeSpecificHeader[0] & 0x04) != 0; 41 | ResidualUnderflow = (OpCodeSpecificHeader[0] & 0x02) != 0; 42 | StatusPresent = (OpCodeSpecificHeader[0] & 0x01) != 0; 43 | 44 | Status = (SCSIStatusCodeName)OpCodeSpecificHeader[2]; 45 | 46 | LUN = new LUNStructure(LUNOrOpCodeSpecific, 0); 47 | 48 | TargetTransferTag = BigEndianConverter.ToUInt32(OpCodeSpecific, 0); 49 | StatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4); 50 | ExpCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8); 51 | MaxCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 12); 52 | DataSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 16); 53 | BufferOffset = BigEndianConverter.ToUInt32(OpCodeSpecific, 20); 54 | ResidualCount = BigEndianConverter.ToUInt32(OpCodeSpecific, 24); 55 | } 56 | 57 | public override byte[] GetBytes() 58 | { 59 | if (Acknowledge) 60 | { 61 | OpCodeSpecificHeader[0] |= 0x40; 62 | } 63 | if (ResidualOverflow) 64 | { 65 | OpCodeSpecificHeader[0] |= 0x04; 66 | } 67 | if (ResidualUnderflow) 68 | { 69 | OpCodeSpecificHeader[0] |= 0x02; 70 | } 71 | if (StatusPresent) 72 | { 73 | OpCodeSpecificHeader[0] |= 0x01; 74 | // If this bit is set to 1, the F bit MUST also be set to 1. 75 | Final = true; 76 | } 77 | 78 | OpCodeSpecificHeader[2] = (byte)Status; 79 | 80 | LUNOrOpCodeSpecific = LUN.GetBytes(); 81 | 82 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 0, TargetTransferTag); 83 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 4, StatSN); 84 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 8, ExpCmdSN); 85 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 12, MaxCmdSN); 86 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 16, DataSN); 87 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 20, BufferOffset); 88 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 24, ResidualCount); 89 | 90 | return base.GetBytes(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/SCSIDataOutPDU.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using SCSI; 11 | using Utilities; 12 | 13 | namespace ISCSI 14 | { 15 | // Data-Out = Data sent to the target (WRITE operations) 16 | public class SCSIDataOutPDU : ISCSIPDU 17 | { 18 | public LUNStructure LUN; 19 | public uint TargetTransferTag; 20 | public uint ExpStatSN; 21 | public uint DataSN; 22 | public uint BufferOffset; 23 | 24 | public SCSIDataOutPDU() 25 | { 26 | OpCode = ISCSIOpCodeName.SCSIDataOut; 27 | } 28 | 29 | public SCSIDataOutPDU(byte[] buffer, int offset) : base(buffer, offset) 30 | { 31 | LUN = new LUNStructure(LUNOrOpCodeSpecific, 0); 32 | 33 | TargetTransferTag = BigEndianConverter.ToUInt32(OpCodeSpecific, 0); 34 | ExpStatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8); 35 | DataSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 16); 36 | BufferOffset = BigEndianConverter.ToUInt32(OpCodeSpecific, 20); 37 | } 38 | 39 | public override byte[] GetBytes() 40 | { 41 | LUNOrOpCodeSpecific = LUN.GetBytes(); 42 | 43 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 0, TargetTransferTag); 44 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 8, ExpStatSN); 45 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 16, DataSN); 46 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 20, BufferOffset); 47 | 48 | return base.GetBytes(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/SCSIResponsePDU.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using SCSI; 11 | using Utilities; 12 | 13 | namespace ISCSI 14 | { 15 | public class SCSIResponsePDU : ISCSIPDU 16 | { 17 | public bool BidirectionalReadResidualOverflow; 18 | public bool BidirectionalReadResidualUnderflow; 19 | public bool ResidualOverflow; 20 | public bool ResidualUnderflow; 21 | public ISCSIResponseName Response; 22 | public SCSIStatusCodeName Status; 23 | public uint SNACKTag; 24 | public uint StatSN; 25 | public uint ExpCmdSN; 26 | public uint MaxCmdSN; 27 | public uint ExpDataSN; 28 | public uint BidirectionalReadResidualCount; 29 | public uint ResidualCount; 30 | 31 | public SCSIResponsePDU() : base() 32 | { 33 | OpCode = ISCSIOpCodeName.SCSIResponse; 34 | Final = true; 35 | } 36 | 37 | public SCSIResponsePDU(byte[] buffer, int offset) : base(buffer, offset) 38 | { 39 | BidirectionalReadResidualOverflow = (OpCodeSpecificHeader[0] & 0x10) != 0; 40 | BidirectionalReadResidualUnderflow = (OpCodeSpecificHeader[0] & 0x08) != 0; 41 | ResidualOverflow = (OpCodeSpecificHeader[0] & 0x04) != 0; 42 | ResidualUnderflow = (OpCodeSpecificHeader[0] & 0x02) != 0; 43 | Response = (ISCSIResponseName)OpCodeSpecificHeader[1]; 44 | Status = (SCSIStatusCodeName)OpCodeSpecificHeader[2]; 45 | 46 | SNACKTag = BigEndianConverter.ToUInt32(OpCodeSpecific, 0); 47 | StatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4); 48 | ExpCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8); 49 | MaxCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 12); 50 | ExpDataSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 16); 51 | BidirectionalReadResidualCount = BigEndianConverter.ToUInt32(OpCodeSpecific, 20); 52 | ResidualCount = BigEndianConverter.ToUInt32(OpCodeSpecific, 24); 53 | } 54 | 55 | public override byte[] GetBytes() 56 | { 57 | if (BidirectionalReadResidualOverflow) 58 | { 59 | OpCodeSpecificHeader[0] |= 0x10; 60 | } 61 | if (BidirectionalReadResidualUnderflow) 62 | { 63 | OpCodeSpecificHeader[0] |= 0x08; 64 | } 65 | if (ResidualOverflow) 66 | { 67 | OpCodeSpecificHeader[0] |= 0x04; 68 | } 69 | if (ResidualUnderflow) 70 | { 71 | OpCodeSpecificHeader[0] |= 0x02; 72 | } 73 | OpCodeSpecificHeader[1] = (byte)Response; 74 | OpCodeSpecificHeader[2] = (byte)Status; 75 | 76 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 0, SNACKTag); 77 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 4, StatSN); 78 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 8, ExpCmdSN); 79 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 12, MaxCmdSN); 80 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 16, ExpDataSN); 81 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 20, BidirectionalReadResidualCount); 82 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 24, ResidualCount); 83 | 84 | return base.GetBytes(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/TextRequestPDU.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using SCSI; 11 | using Utilities; 12 | 13 | namespace ISCSI 14 | { 15 | public class TextRequestPDU : ISCSIPDU 16 | { 17 | public bool Continue; 18 | public LUNStructure LUN; 19 | public uint TargetTransferTag; 20 | public uint CmdSN; 21 | public uint ExpStatSN; 22 | 23 | public string Text = String.Empty; 24 | 25 | public TextRequestPDU() : base() 26 | { 27 | OpCode = ISCSIOpCodeName.TextRequest; 28 | } 29 | 30 | public TextRequestPDU(byte[] buffer, int offset) : base(buffer, offset) 31 | { 32 | Continue = (OpCodeSpecificHeader[0] & 0x40) != 0; 33 | 34 | LUN = new LUNStructure(LUNOrOpCodeSpecific, 0); 35 | 36 | TargetTransferTag = BigEndianConverter.ToUInt32(OpCodeSpecific, 0); 37 | CmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4); 38 | ExpStatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8); 39 | 40 | Text = ASCIIEncoding.ASCII.GetString(Data); 41 | } 42 | 43 | public override byte[] GetBytes() 44 | { 45 | if (Continue) 46 | { 47 | OpCodeSpecificHeader[0] |= 0x40; 48 | } 49 | 50 | LUNOrOpCodeSpecific = LUN.GetBytes(); 51 | 52 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 0, TargetTransferTag); 53 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 4, CmdSN); 54 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 8, ExpStatSN); 55 | 56 | Data = ASCIIEncoding.ASCII.GetBytes(Text); 57 | 58 | return base.GetBytes(); 59 | } 60 | 61 | public List> TextParameters 62 | { 63 | set 64 | { 65 | Text = KeyValuePairUtils.ToNullDelimitedString(value); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.PDU/TextResponsePDU.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using SCSI; 11 | using Utilities; 12 | 13 | namespace ISCSI 14 | { 15 | public class TextResponsePDU : ISCSIPDU 16 | { 17 | public bool Continue; 18 | public LUNStructure LUN; 19 | public uint TargetTransferTag; 20 | public uint StatSN; 21 | public uint ExpCmdSN; 22 | public uint MaxCmdSN; 23 | 24 | public string Text = String.Empty; 25 | 26 | public TextResponsePDU() : base() 27 | { 28 | OpCode = ISCSIOpCodeName.TextResponse; 29 | } 30 | 31 | public TextResponsePDU(byte[] buffer, int offset) : base(buffer, offset) 32 | { 33 | Continue = (OpCodeSpecificHeader[0] & 0x40) != 0; 34 | 35 | LUN = new LUNStructure(LUNOrOpCodeSpecific, 0); 36 | 37 | TargetTransferTag = BigEndianConverter.ToUInt32(OpCodeSpecific, 0); 38 | StatSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 4); 39 | ExpCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 8); 40 | MaxCmdSN = BigEndianConverter.ToUInt32(OpCodeSpecific, 12); 41 | 42 | Text = ASCIIEncoding.ASCII.GetString(Data); 43 | } 44 | 45 | public override byte[] GetBytes() 46 | { 47 | if (Continue) 48 | { 49 | OpCodeSpecificHeader[0] |= 0x40; 50 | } 51 | 52 | LUNOrOpCodeSpecific = LUN.GetBytes(); 53 | 54 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 0, TargetTransferTag); 55 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 4, StatSN); 56 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 8, ExpCmdSN); 57 | BigEndianWriter.WriteUInt32(OpCodeSpecific, 12, MaxCmdSN); 58 | 59 | Data = ASCIIEncoding.ASCII.GetBytes(Text); 60 | 61 | return base.GetBytes(); 62 | } 63 | 64 | public List> TextParameters 65 | { 66 | set 67 | { 68 | Text = KeyValuePairUtils.ToNullDelimitedString(value); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Server/ConnectionState.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Net.Sockets; 10 | using System.Text; 11 | using SCSI; 12 | using Utilities; 13 | 14 | namespace ISCSI.Server 15 | { 16 | /// 17 | /// iSCSI Connection state object 18 | /// 19 | internal class ConnectionState 20 | { 21 | public Socket ClientSocket = null; 22 | public static int ReceiveBufferSize = ISCSIPDU.BasicHeaderSegmentLength + ISCSIServer.DeclaredParameters.MaxRecvDataSegmentLength; 23 | public ISCSIConnectionReceiveBuffer ReceiveBuffer = new ISCSIConnectionReceiveBuffer(ReceiveBufferSize); 24 | 25 | public ConnectionParameters ConnectionParameters = new ConnectionParameters(); 26 | 27 | public CountdownLatch RunningSCSICommands = new CountdownLatch(); 28 | public BlockingQueue SendQueue = new BlockingQueue(); 29 | 30 | public void OnCommandCompleted(SCSIStatusCodeName status, byte[] responseBytes, object task) 31 | { 32 | RunningSCSICommands.Decrement(); 33 | SCSICommandPDU command = (SCSICommandPDU)task; 34 | List responseList = TargetResponseHelper.PrepareSCSICommandResponse(command, status, responseBytes, ConnectionParameters); 35 | SendQueue.Enqueue(responseList); 36 | } 37 | 38 | public string ConnectionIdentifier 39 | { 40 | get 41 | { 42 | return ConnectionParameters.ConnectionIdentifier; 43 | } 44 | } 45 | 46 | public ISCSISession Session 47 | { 48 | get 49 | { 50 | return ConnectionParameters.Session; 51 | } 52 | } 53 | 54 | public ISCSITarget Target 55 | { 56 | get 57 | { 58 | return Session.Target; 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Server/Exceptions/InvalidTargetTransferTagException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace ISCSI.Server 12 | { 13 | internal class InvalidTargetTransferTagException : Exception 14 | { 15 | public uint TargetTransferTag; 16 | 17 | public InvalidTargetTransferTagException(uint targetTransferTag) : base("Invalid TargetTransferTag: " + targetTransferTag) 18 | { 19 | TargetTransferTag = targetTransferTag; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Server/ISCSIServer.Parameters.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | 9 | namespace ISCSI.Server 10 | { 11 | public partial class ISCSIServer 12 | { 13 | /// 14 | /// - CommandQueueSize = 0 means the initiator can send one command at a time (because MaxCmdSN = ExpCmdSN + CommandQueueSize), 15 | /// (in this case there won't be any queue following the currently processed command). 16 | /// - Over a low-latency connection, most of the gain comes from increasing the queue size from 0 to 1 17 | /// - CmdSN is session wide, so CommandQueueSize is a session parameter. 18 | /// 19 | public static uint DefaultCommandQueueSize = 64; 20 | 21 | public class DesiredParameters 22 | { 23 | // Session parameters that will be offered to the initiator: 24 | public static int MaxConnections = 1; // implementation limit 25 | public static bool InitialR2T = false; 26 | public static bool ImmediateData = true; 27 | public static int MaxBurstLength = DefaultParameters.Session.MaxBurstLength; 28 | public static int FirstBurstLength = DefaultParameters.Session.FirstBurstLength; 29 | public static int DefaultTime2Wait = 0; 30 | public static int DefaultTime2Retain = 20; 31 | public static int MaxOutstandingR2T = 16; 32 | public static bool DataPDUInOrder = true; // implementation limit 33 | public static bool DataSequenceInOrder = true; // implementation limit 34 | public static int ErrorRecoveryLevel = 0; // implementation limit 35 | } 36 | 37 | public class DeclaredParameters 38 | { 39 | // Connection parameters: 40 | public static int MaxRecvDataSegmentLength = 262144; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Server/ISCSIServer.TextRequest.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace ISCSI.Server 13 | { 14 | public partial class ISCSIServer 15 | { 16 | private TextResponsePDU GetTextResponsePDU(TextRequestPDU request, ConnectionParameters connection) 17 | { 18 | TextResponsePDU response = new TextResponsePDU(); 19 | response.Final = request.Final; 20 | response.InitiatorTaskTag = request.InitiatorTaskTag; 21 | if (request.Continue) 22 | { 23 | connection.AddTextToSequence(request.InitiatorTaskTag, request.Text); 24 | } 25 | else 26 | { 27 | string text = connection.AddTextToSequence(request.InitiatorTaskTag, request.Text); 28 | connection.RemoveTextSequence(request.InitiatorTaskTag); 29 | KeyValuePairList requestParameters = KeyValuePairUtils.GetKeyValuePairList(text); 30 | // text keys are case sensitive 31 | if (requestParameters.ContainsKey("SendTargets")) 32 | { 33 | KeyValuePairList responseParameters = new KeyValuePairList(); 34 | lock (m_targets.Lock) 35 | { 36 | foreach (ISCSITarget target in m_targets.GetList()) 37 | { 38 | responseParameters.Add("TargetName", target.TargetName); 39 | } 40 | } 41 | response.TextParameters = responseParameters; 42 | } 43 | else if (connection.Session.IsDiscovery || !IsVendorSpecificRequest(requestParameters)) 44 | { 45 | KeyValuePairList responseParameters = new KeyValuePairList(); 46 | foreach (KeyValuePair entry in requestParameters) 47 | { 48 | responseParameters.Add(entry.Key, "Reject"); 49 | } 50 | response.TextParameters = responseParameters; 51 | } 52 | else 53 | { 54 | // RFC 3720: Vendor specific keys MUST ONLY be used in normal sessions 55 | // Vendor specific text request, let the target handle it: 56 | response.TextParameters = connection.Session.Target.GetTextResponse(requestParameters); 57 | } 58 | } 59 | return response; 60 | } 61 | 62 | private static bool IsVendorSpecificRequest(KeyValuePairList requestParameters) 63 | { 64 | foreach(string key in requestParameters.Keys) 65 | { 66 | // RFC 3720: Implementers may introduce new keys by prefixing them with "X-" [..] or X# if registered with IANA. 67 | if (!(key.StartsWith("X-") || key.StartsWith("X#"))) 68 | { 69 | return false; 70 | } 71 | } 72 | return (requestParameters.Count > 0); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Server/ServerResponseHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace ISCSI.Server 13 | { 14 | internal class ServerResponseHelper 15 | { 16 | internal static NOPInPDU GetKeepAlivePDU() 17 | { 18 | NOPInPDU response = new NOPInPDU(); 19 | // when a target sends a NOP-In that is not a response to a Nop-Out received from the 20 | // initiator, the Initiator Task Tag MUST be set to 0xffffffff. 21 | response.InitiatorTaskTag = 0xFFFFFFFF; 22 | // Target Transfer Tag: If the target is initiating a NOP-In without wanting to receive a 23 | // corresponding NOP-Out, this field MUST hold the reserved value of 0xffffffff. 24 | response.TargetTransferTag = 0xFFFFFFFF; 25 | return response; 26 | } 27 | 28 | internal static NOPInPDU GetNOPResponsePDU(NOPOutPDU request) 29 | { 30 | NOPInPDU response = new NOPInPDU(); 31 | response.Data = request.Data; 32 | // When a target receives the NOP-Out with a valid Initiator Task Tag (not the reserved value 0xffffffff), 33 | // it MUST respond with a NOP-In with the same Initiator Task Tag that was provided in the NOP-Out request. 34 | // For such a response, the Target Transfer Tag MUST be 0xffffffff. 35 | response.InitiatorTaskTag = request.InitiatorTaskTag; 36 | response.TargetTransferTag = 0xFFFFFFFF; 37 | return response; 38 | } 39 | 40 | internal static LogoutResponsePDU GetLogoutResponsePDU(LogoutRequestPDU request, LogoutResponse responseCode) 41 | { 42 | LogoutResponsePDU response = new LogoutResponsePDU(); 43 | response.Response = responseCode; 44 | response.Final = true; 45 | response.InitiatorTaskTag = request.InitiatorTaskTag; 46 | return response; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Server/TargetEventArgs/AuthorizationRequestArgs.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Net; 9 | 10 | namespace ISCSI.Server 11 | { 12 | public class AuthorizationRequestArgs : EventArgs 13 | { 14 | public string InitiatorName; 15 | public ulong ISID; 16 | public IPEndPoint InitiatorEndPoint; 17 | public bool Accept = true; 18 | 19 | public AuthorizationRequestArgs(string initiatorName, ulong isid, IPEndPoint initiatorEndPoint) 20 | { 21 | InitiatorName = initiatorName; 22 | ISID = isid; 23 | InitiatorEndPoint = initiatorEndPoint; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Server/TargetEventArgs/SessionTerminationArgs.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | 9 | namespace ISCSI.Server 10 | { 11 | public enum SessionTerminationReason 12 | { 13 | Logout, 14 | ImplicitLogout, // Session reinstatement 15 | ConnectionFailure, 16 | TargetReset, 17 | } 18 | 19 | public class SessionTerminationArgs : EventArgs 20 | { 21 | public string InitiatorName; 22 | public ulong ISID; 23 | public SessionTerminationReason Reason; 24 | 25 | public SessionTerminationArgs(string initiatorName, ulong isid, SessionTerminationReason reason) 26 | { 27 | InitiatorName = initiatorName; 28 | ISID = isid; 29 | Reason = reason; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Server/TargetEventArgs/TextRequestArgs.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2024 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace ISCSI.Server 11 | { 12 | public class TextRequestArgs : EventArgs 13 | { 14 | public List> RequestParaemeters; 15 | public List> ResponseParaemeters = new List>(); 16 | 17 | public TextRequestArgs(List> requestParaemeters) 18 | { 19 | RequestParaemeters = requestParaemeters; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.Server/TargetList.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using System.Threading; 11 | 12 | namespace ISCSI.Server 13 | { 14 | internal class TargetList 15 | { 16 | public object Lock = new object(); 17 | private List m_targets = new List(); 18 | 19 | public void AddTarget(ISCSITarget target) 20 | { 21 | lock (Lock) 22 | { 23 | int index = IndexOfTarget(target.TargetName); 24 | if (index >= 0) 25 | { 26 | throw new ArgumentException("A target with the same iSCSI Target Name already exists"); 27 | } 28 | m_targets.Add(target); 29 | } 30 | } 31 | 32 | public ISCSITarget FindTarget(string targetName) 33 | { 34 | lock (Lock) 35 | { 36 | int index = IndexOfTarget(targetName); 37 | if (index >= 0) 38 | { 39 | return m_targets[index]; 40 | } 41 | return null; 42 | } 43 | } 44 | 45 | public bool RemoveTarget(string targetName) 46 | { 47 | lock (Lock) 48 | { 49 | int index = IndexOfTarget(targetName); 50 | if (index >= 0) 51 | { 52 | m_targets.RemoveAt(index); 53 | return true; 54 | } 55 | return false; 56 | } 57 | } 58 | 59 | /// 60 | /// Caller MUST obtain a lock on TargetList.Lock before calling this method 61 | /// 62 | public List GetList() 63 | { 64 | return new List(m_targets); 65 | } 66 | 67 | private int IndexOfTarget(string targetName) 68 | { 69 | for (int index = 0; index < m_targets.Count; index++) 70 | { 71 | if (String.Equals(m_targets[index].TargetName, targetName, StringComparison.OrdinalIgnoreCase)) 72 | { 73 | return index; 74 | } 75 | } 76 | return -1; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ISCSI/ISCSI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net20;net40;netstandard2.0 5 | ISCSI 6 | iSCSI Library 7 | iSCSI Library 8 | 1.5.5.1 9 | 1573;1591 10 | ISCSI 11 | false 12 | Tal Aloni 13 | Copyright © Tal Aloni 2012-2024 14 | ISCSILibrary is an open-source C# library for creating iSCSI Target server and client implementations 15 | LGPL-3.0-or-later 16 | https://github.com/TalAloni/iSCSIConsole 17 | https://github.com/TalAloni/iSCSIConsole 18 | true 19 | Debug;Release 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /ISCSI/SCSI/Enums/AddressingMethod.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SCSI 3 | { 4 | public enum AddressingMethod 5 | { 6 | PeripheralDeviceAddressing = 0x0, 7 | FlatSpaceAddressing = 0x1, 8 | LogicalUnitAddressing = 0x2, 9 | ExtendedLogicalUnitAddressing = 0x3, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ISCSI/SCSI/Enums/ModePageCodeName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SCSI 3 | { 4 | public enum ModePageCodeName : byte 5 | { 6 | VendorSpecificPage = 0x00, // Windows 2000 will request this page 7 | CachingParametersPage = 0x08, 8 | ControlModePage = 0x0A, 9 | PowerConditionModePage = 0x1A, 10 | InformationalExceptionsControlModePage = 0x1C, 11 | ReturnAllPages = 0x3F, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ISCSI/SCSI/Enums/PeripheralDeviceType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SCSI 3 | { 4 | public enum PeripheralDeviceType : byte 5 | { 6 | DirectAccessBlockDevice = 0x00, 7 | SequentialAccessDevice = 0x01, 8 | CDRomDevice = 0x05, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ISCSI/SCSI/Enums/SCSIOpCodeName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SCSI 3 | { 4 | public enum SCSIOpCodeName : byte 5 | { 6 | TestUnitReady = 0x00, 7 | Rewind = 0x01, 8 | RequestSense = 0x03, 9 | FormatUnit = 0x04, 10 | ReadBlockLimits = 0x05, 11 | Read6 = 0x08, 12 | Write6 = 0x0A, 13 | SetCapacity = 0x0B, 14 | ReadReverse6 = 0x0F, 15 | WriteFilemarks6 = 0x10, 16 | Space6 = 0x11, 17 | Inquiry = 0x12, 18 | Verify6 = 0x13, 19 | RecoverBufferedData = 0x14, 20 | ModeSelect6 = 0x15, 21 | Reserve6 = 0x16, 22 | Release6 = 0x17, 23 | Erase6 = 0x19, 24 | ModeSense6 = 0x1A, 25 | LoadUnload = 0x1B, 26 | StartStopUnit = 0x1B, 27 | ReceiveDiagnosticResults = 0x1C, 28 | ReadCapacity10 = 0x25, 29 | Read10 = 0x28, 30 | Write10 = 0x2A, 31 | Locate10 = 0x2B, 32 | WriteAndVerify10 = 0x2E, 33 | Verify10 = 0x2F, 34 | PreFetch10 = 0x34, 35 | ReadPosition = 0x34, 36 | ReadDefectData10 = 0x37, 37 | WriteBuffer = 0x3B, 38 | ReadBuffer10 = 0x3C, 39 | ReadBuffer = 0x3C, 40 | SynchronizeCache10 = 0x35, 41 | ReadLong10 = 0x3E, 42 | WriteLong10 = 0x3F, 43 | WriteSame10 = 0x41, 44 | ReportDensitySupport = 0x44, 45 | LogSelect10 = 0x4C, 46 | LogSense10 = 0x4D, 47 | ModeSelect10 = 0x55, 48 | ModeSense10 = 0x5A, 49 | PersistentReserveIn = 0x5E, 50 | PersistentReserveOut = 0x5F, 51 | VariableLengthCDB = 0x7F, 52 | WriteFilemarks16 = 0x80, 53 | ReadReverse16 = 0x81, 54 | ThirdPartyCopyOut = 0x83, 55 | ThirdPartyCopyIn = 0x84, 56 | Read16 = 0x88, 57 | Write16 = 0x8A, 58 | ReadAttribute16 = 0x8C, 59 | WriteAndVerify16 = 0x8E, 60 | Verify16 = 0x8F, 61 | PreFetch16 = 0x90, 62 | SynchronizeCache16 = 0x91, 63 | Locate16 = 0x92, 64 | Erase16 = 0x93, 65 | WriteSame16 = 0x93, 66 | ServiceActionIn16 = 0x9E, 67 | ServiceActionOut16 = 0x9F, 68 | ReportLUNs = 0xA0, 69 | SecurityProtocolIn = 0xA2, 70 | MaintenanceIn = 0xA3, 71 | Read12 = 0xA8, 72 | ServiceActionOut12 = 0xA9, 73 | Write12 = 0xAA, 74 | ServiceActionIn12 = 0xAB, 75 | WriteAndVerify12 = 0xAE, 76 | Verify12 = 0xAF, 77 | ReadDefectData12 = 0xB7, 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ISCSI/SCSI/Enums/SCSIStatusCodeName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SCSI 3 | { 4 | public enum SCSIStatusCodeName : byte 5 | { 6 | Good = 0x00, 7 | CheckCondition = 0x02, 8 | ConditionMet = 0x04, 9 | Busy = 0x08, 10 | // ReservationConflict = 0x18, 11 | // TaskSetFull = 0x28, 12 | // ACAActive = 0x30, 13 | TaskAborted = 0x40, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ISCSI/SCSI/Enums/ServiceAction.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SCSI 3 | { 4 | public enum ServiceAction : byte 5 | { 6 | None = 0x00, 7 | ReadCapacity16 = 0x10, 8 | ReadLong16 = 0x11, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ISCSI/SCSI/Enums/VersionDescriptorName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SCSI 3 | { 4 | public enum VersionDescriptorName 5 | { 6 | iSCSI = 0x0960, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ISCSI/SCSI/Enums/VitalProductDataPageName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SCSI 3 | { 4 | public enum VitalProductDataPageName : byte 5 | { 6 | SupportedVPDPages = 0x00, 7 | UnitSerialNumber = 0x80, 8 | DeviceIdentification = 0x83, 9 | BlockLimits = 0xB0, 10 | BlockDeviceCharacteristics = 0xB1, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ISCSI/SCSI/Exceptions/UnsupportedSCSICommandException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace SCSI 12 | { 13 | public class UnsupportedSCSICommandException : NotImplementedException 14 | { 15 | public UnsupportedSCSICommandException(string message) : base(message) 16 | { 17 | 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSICommandDescriptorBlock/InquiryCommandDescriptorBlock.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class InquiryCommand : SCSICommandDescriptorBlock 15 | { 16 | public bool EVPD; // Enable Vital Product Data 17 | public VitalProductDataPageName PageCode; 18 | 19 | public InquiryCommand() : base() 20 | { 21 | OpCode = SCSIOpCodeName.Inquiry; 22 | } 23 | 24 | public InquiryCommand(byte[] buffer, int offset) 25 | { 26 | OpCode = (SCSIOpCodeName)buffer[offset + 0]; 27 | EVPD = (buffer[offset + 1] & 0x01) != 0; 28 | PageCode = (VitalProductDataPageName)buffer[offset + 2]; 29 | AllocationLength = BigEndianConverter.ToUInt16(buffer, offset + 3); 30 | Control = buffer[offset + 5]; 31 | } 32 | 33 | public override byte[] GetBytes() 34 | { 35 | byte[] buffer = new byte[6]; 36 | buffer[0] = (byte)OpCode; 37 | if (EVPD) 38 | { 39 | buffer[1] |= 0x01; 40 | } 41 | buffer[2] = (byte)PageCode; 42 | BigEndianWriter.WriteUInt16(buffer, 3, AllocationLength); 43 | buffer[5] = Control; 44 | return buffer; 45 | } 46 | 47 | public ushort AllocationLength 48 | { 49 | get 50 | { 51 | return (ushort)TransferLength; 52 | } 53 | set 54 | { 55 | TransferLength = value; 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSICommandDescriptorBlock/ModeSense6CommandDescriptorBlock.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class ModeSense6CommandDescriptorBlock : SCSICommandDescriptorBlock 15 | { 16 | public bool DBD; // Disable block descriptors 17 | public byte PC; // Page Control 18 | public ModePageCodeName PageCode; 19 | public byte SubpageCode; 20 | 21 | public ModeSense6CommandDescriptorBlock() : base() 22 | { 23 | OpCode = SCSIOpCodeName.ModeSense6; 24 | } 25 | 26 | public ModeSense6CommandDescriptorBlock(byte[] buffer, int offset) : base() 27 | { 28 | OpCode = (SCSIOpCodeName)buffer[offset + 0]; 29 | DBD = (buffer[offset + 1] & 0x08) != 0; 30 | PC = (byte)(buffer[offset + 2] >> 6); 31 | PageCode = (ModePageCodeName)(buffer[offset + 2] & 0x3F); 32 | SubpageCode = buffer[offset + 3]; 33 | AllocationLength = buffer[offset + 4]; 34 | Control = buffer[offset + 5]; 35 | } 36 | 37 | public override byte[] GetBytes() 38 | { 39 | byte[] buffer = new byte[6]; 40 | buffer[0] = (byte)OpCode; 41 | if (DBD) 42 | { 43 | buffer[1] |= 0x08; 44 | } 45 | buffer[2] |= (byte)(PC << 6); 46 | buffer[2] |= (byte)((byte)PageCode & 0x3F); 47 | buffer[3] = SubpageCode; 48 | buffer[4] = AllocationLength; 49 | buffer[5] = Control; 50 | return buffer; 51 | } 52 | 53 | public byte AllocationLength 54 | { 55 | get 56 | { 57 | return (byte)TransferLength; 58 | } 59 | set 60 | { 61 | TransferLength = value; 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSICommandDescriptorBlock/SCSICommandDescriptorBlock10.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | /// 15 | /// 10-byte SCSI CDB 16 | /// 17 | public class SCSICommandDescriptorBlock10 : SCSICommandDescriptorBlock 18 | { 19 | public SCSICommandDescriptorBlock10(SCSIOpCodeName opCode) : base() 20 | { 21 | this.OpCode = opCode; 22 | } 23 | 24 | public SCSICommandDescriptorBlock10(byte[] buffer, int offset) : base() 25 | { 26 | OpCode = (SCSIOpCodeName)buffer[offset + 0]; 27 | MiscellaneousCDBInformationHeader = (byte)((buffer[offset + 1] & 0xE0) >> 5); 28 | ServiceAction = (ServiceAction)((buffer[offset + 1] & 0x1F)); 29 | 30 | LogicalBlockAddress = BigEndianConverter.ToUInt32(buffer, offset + 2); 31 | MiscellaneousCDBinformation = buffer[offset + 6]; 32 | TransferLength = BigEndianConverter.ToUInt16(buffer, offset + 7); 33 | Control = buffer[offset + 9]; 34 | } 35 | 36 | public override byte[] GetBytes() 37 | { 38 | byte[] buffer = new byte[10]; 39 | buffer[0] = (byte)OpCode; 40 | buffer[1] |= (byte)(MiscellaneousCDBInformationHeader << 5); 41 | buffer[1] |= (byte)((byte)ServiceAction & 0x1F); 42 | BigEndianWriter.WriteUInt32(buffer, 2, LogicalBlockAddress); 43 | buffer[6] = MiscellaneousCDBinformation; 44 | BigEndianWriter.WriteUInt16(buffer, 7, (ushort)TransferLength); 45 | buffer[9] = Control; 46 | return buffer; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSICommandDescriptorBlock/SCSICommandDescriptorBlock12.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | /// 15 | /// 12-byte SCSI CDB 16 | /// 17 | public class SCSICommandDescriptorBlock12 : SCSICommandDescriptorBlock 18 | { 19 | public SCSICommandDescriptorBlock12(SCSIOpCodeName opCode) : base() 20 | { 21 | this.OpCode = opCode; 22 | } 23 | 24 | public SCSICommandDescriptorBlock12(byte[] buffer, int offset) : base() 25 | { 26 | OpCode = (SCSIOpCodeName)buffer[offset + 0]; 27 | MiscellaneousCDBInformationHeader = (byte)((buffer[offset + 1] & 0xE0) >> 5); 28 | ServiceAction = (ServiceAction)((buffer[offset + 1] & 0x1F)); 29 | 30 | LogicalBlockAddress = BigEndianConverter.ToUInt32(buffer, offset + 2); 31 | TransferLength = BigEndianConverter.ToUInt32(buffer, offset + 6); 32 | MiscellaneousCDBinformation = buffer[offset + 10]; 33 | Control = buffer[offset + 11]; 34 | } 35 | 36 | public override byte[] GetBytes() 37 | { 38 | byte[] buffer = new byte[16]; 39 | buffer[0] = (byte)OpCode; 40 | buffer[1] |= (byte)(MiscellaneousCDBInformationHeader << 5); 41 | buffer[1] |= (byte)((byte)ServiceAction & 0x1F); 42 | BigEndianWriter.WriteUInt32(buffer, 2, LogicalBlockAddress); 43 | BigEndianWriter.WriteUInt32(buffer, 6, TransferLength); 44 | buffer[10] = MiscellaneousCDBinformation; 45 | buffer[11] = Control; 46 | return buffer; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSICommandDescriptorBlock/SCSICommandDescriptorBlock16.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | /// 15 | /// 16-byte SCSI CDB 16 | /// 17 | public class SCSICommandDescriptorBlock16 : SCSICommandDescriptorBlock 18 | { 19 | public SCSICommandDescriptorBlock16(SCSIOpCodeName opCode) : base() 20 | { 21 | this.OpCode = opCode; 22 | } 23 | 24 | public SCSICommandDescriptorBlock16(byte[] buffer, int offset) : base() 25 | { 26 | OpCode = (SCSIOpCodeName)buffer[offset + 0]; 27 | MiscellaneousCDBInformationHeader = (byte)((buffer[offset + 1] & 0xE0) >> 5); 28 | ServiceAction = (ServiceAction)((buffer[offset + 1] & 0x1F)); 29 | 30 | LogicalBlockAddress = BigEndianConverter.ToUInt32(buffer, offset + 2); 31 | AdditionalCDBdata = BigEndianConverter.ToUInt32(buffer, offset + 6); 32 | TransferLength = BigEndianConverter.ToUInt32(buffer, offset + 10); 33 | MiscellaneousCDBinformation = buffer[offset + 14]; 34 | Control = buffer[offset + 15]; 35 | } 36 | 37 | public override byte[] GetBytes() 38 | { 39 | byte[] buffer = new byte[16]; 40 | buffer[0] = (byte)OpCode; 41 | buffer[1] |= (byte)(MiscellaneousCDBInformationHeader << 5); 42 | buffer[1] |= (byte)((byte)ServiceAction & 0x1F); 43 | BigEndianWriter.WriteUInt32(buffer, 2, LogicalBlockAddress); 44 | BigEndianWriter.WriteUInt32(buffer, 6, AdditionalCDBdata); 45 | BigEndianWriter.WriteUInt32(buffer, 10, TransferLength); 46 | buffer[14] = MiscellaneousCDBinformation; 47 | buffer[15] = Control; 48 | return buffer; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSICommandDescriptorBlock/SCSICommandDescriptorBlock6.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2017 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | /// 15 | /// 6-byte SCSI CDB 16 | /// 17 | public class SCSICommandDescriptorBlock6 : SCSICommandDescriptorBlock 18 | { 19 | public SCSICommandDescriptorBlock6(SCSIOpCodeName opCode) : base() 20 | { 21 | this.OpCode = opCode; 22 | } 23 | 24 | public SCSICommandDescriptorBlock6(byte[] buffer, int offset) : base() 25 | { 26 | OpCode = (SCSIOpCodeName)buffer[offset + 0]; 27 | MiscellaneousCDBInformationHeader = (byte)((buffer[offset + 1] & 0xE0) >> 5); 28 | 29 | uint temp = BigEndianReader.ReadUInt24(buffer, offset + 1); 30 | LogicalBlockAddress = temp & 0x1FFFFF; 31 | TransferLength = buffer[offset + 4]; 32 | Control = buffer[offset + 5]; 33 | } 34 | 35 | public override byte[] GetBytes() 36 | { 37 | byte[] buffer = new byte[6]; 38 | buffer[0] = (byte)OpCode; 39 | buffer[1] |= (byte)(MiscellaneousCDBInformationHeader << 5); 40 | buffer[1] |= (byte)((LogicalBlockAddress >> 16) & 0x1F); 41 | buffer[2] = (byte)((LogicalBlockAddress >> 8) & 0xFF); 42 | buffer[3] = (byte)((LogicalBlockAddress >> 0) & 0xFF); 43 | buffer[4] = (byte)TransferLength; 44 | buffer[5] = Control; 45 | return buffer; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ModePages/ControlModePage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class ControlModePage : ModePage0 15 | { 16 | public byte TST; // 3 bits 17 | public bool TmfOnly; 18 | public bool Reserved1; 19 | public bool DSense; 20 | public bool GLTSD; 21 | public bool RLEC; 22 | public byte QueueAlgorithmModifier; // 4 bits 23 | public bool Reserved2; 24 | public byte QErr; // 2 bits 25 | public bool DQUE; // obsolete 26 | public bool VS; // obsolete 27 | public bool RAC; // obsolete 28 | public bool UA_INTLCK_CTRL; // obsolete 29 | public bool SWP; 30 | public bool RAERP; // obsolete 31 | public bool UAAERP; // obsolete 32 | public bool EAERP; // obsolete 33 | public bool ATO; 34 | public bool TAS; 35 | public byte Reserved3; // 3 bits 36 | public byte AutoLoadMode; // 3 bits 37 | public ushort Obsolete1; 38 | public ushort Obsolete2; 39 | public ushort Obsolete3; 40 | 41 | public ControlModePage() : base(ModePageCodeName.ControlModePage, 10) 42 | { 43 | } 44 | 45 | public ControlModePage(byte[] buffer, int offset) : base(buffer, offset) 46 | { 47 | throw new NotImplementedException(); 48 | } 49 | 50 | public override byte[] GetBytes() 51 | { 52 | byte[] buffer = base.GetBytes(); 53 | buffer[2] = (byte)(TST << 5 | Convert.ToByte(TmfOnly) << 4 | Convert.ToByte(Reserved1) << 3 | Convert.ToByte(DSense) << 2 | Convert.ToByte(GLTSD) << 1 | Convert.ToByte(RLEC)); 54 | buffer[3] = (byte)(QueueAlgorithmModifier << 4 | Convert.ToByte(Reserved2) << 3 | (Convert.ToByte(QErr) & 0x3) << 1 | Convert.ToByte(DQUE)); 55 | buffer[4] = (byte)(Convert.ToByte(VS) << 7 | Convert.ToByte(RAC) << 6 | (Convert.ToByte(UA_INTLCK_CTRL) & 0x3) << 4 | Convert.ToByte(SWP) << 3 | Convert.ToByte(RAERP) << 2 | Convert.ToByte(UAAERP) << 1 | Convert.ToByte(EAERP)); 56 | buffer[5] = (byte)(Convert.ToByte(ATO) << 7 | Convert.ToByte(TAS) << 6 | (Convert.ToByte(Reserved3) & 0x7) << 3 | (Convert.ToByte(AutoLoadMode) & 0x7)); 57 | BigEndianWriter.WriteUInt16(buffer, 6, Obsolete1); 58 | BigEndianWriter.WriteUInt16(buffer, 8, Obsolete2); 59 | BigEndianWriter.WriteUInt16(buffer, 10, Obsolete3); 60 | 61 | return buffer; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ModePages/InformationalExceptionsControlModePage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class InformationalExceptionsControlModePage : ModePage0 15 | { 16 | public bool PERF; // Performance 17 | public bool EBF; // Enable Background Function 18 | public bool EWASC; // Enable Warning 19 | public bool DExcpt; // Disable Exception Control 20 | public bool Test; 21 | public bool EBackErr; // enable background error 22 | public bool LogErr; 23 | 24 | public byte MRIE; 25 | public uint IntervalTimer; 26 | public uint ReportCount; 27 | 28 | public InformationalExceptionsControlModePage() : base(ModePageCodeName.InformationalExceptionsControlModePage, 10) 29 | { 30 | } 31 | 32 | public InformationalExceptionsControlModePage(byte[] buffer, int offset) : base(buffer, offset) 33 | { 34 | PERF = (buffer[offset + 2] & 0x80) != 0; 35 | EBF = (buffer[offset + 2] & 0x20) != 0; 36 | EWASC = (buffer[offset + 2] & 0x10) != 0; 37 | DExcpt = (buffer[offset + 2] & 0x08) != 0; 38 | Test = (buffer[offset + 2] & 0x04) != 0; 39 | EBackErr = (buffer[offset + 2] & 0x02) != 0; 40 | LogErr = (buffer[offset + 2] & 0x01) != 0; 41 | 42 | MRIE = (byte)(buffer[offset + 3] & 0x0F); 43 | IntervalTimer = BigEndianConverter.ToUInt32(buffer, 4); 44 | ReportCount = BigEndianConverter.ToUInt32(buffer, 8); 45 | } 46 | 47 | public override byte[] GetBytes() 48 | { 49 | byte[] buffer = base.GetBytes(); 50 | if (PERF) 51 | { 52 | buffer[2] |= 0x80; 53 | } 54 | if (EBF) 55 | { 56 | buffer[2] |= 0x20; 57 | } 58 | if (EWASC) 59 | { 60 | buffer[2] |= 0x10; 61 | } 62 | if (DExcpt) 63 | { 64 | buffer[2] |= 0x08; 65 | } 66 | if (Test) 67 | { 68 | buffer[2] |= 0x04; 69 | } 70 | if (EBackErr) 71 | { 72 | buffer[2] |= 0x02; 73 | } 74 | if (LogErr) 75 | { 76 | buffer[2] |= 0x01; 77 | } 78 | 79 | buffer[3] = (byte)(MRIE & 0x0F); 80 | BigEndianWriter.WriteUInt32(buffer, 4, IntervalTimer); 81 | BigEndianWriter.WriteUInt32(buffer, 8, ReportCount); 82 | 83 | return buffer; 84 | } 85 | } 86 | } 87 | 88 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ModePages/LongLBAModeParameterBlockDescriptor.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class LongLBAModeParameterBlockDescriptor 15 | { 16 | public const int Length = 16; 17 | 18 | public ulong NumberOfBlocks; 19 | public uint Reserved; 20 | public uint LogicalBlockLength; 21 | 22 | public LongLBAModeParameterBlockDescriptor() 23 | { 24 | } 25 | 26 | public LongLBAModeParameterBlockDescriptor(byte[] buffer, int offset) 27 | { 28 | NumberOfBlocks = BigEndianConverter.ToUInt64(buffer, offset + 0); 29 | Reserved = BigEndianConverter.ToUInt32(buffer, offset + 8); 30 | LogicalBlockLength = BigEndianConverter.ToUInt32(buffer, offset + 12); 31 | } 32 | 33 | public byte[] GetBytes() 34 | { 35 | byte[] buffer = new byte[Length]; 36 | BigEndianWriter.WriteUInt64(buffer, 0, NumberOfBlocks); 37 | BigEndianWriter.WriteUInt32(buffer, 8, Reserved); 38 | BigEndianWriter.WriteUInt32(buffer, 12, LogicalBlockLength); 39 | return buffer; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ModePages/ModePage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace SCSI 12 | { 13 | public abstract class ModePage // General Mode Page, this is either ModePage0, SubModePage or vendor specific page 14 | { 15 | public abstract byte[] GetBytes(); 16 | 17 | public abstract int Length 18 | { 19 | get; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ModePages/ModePage0.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class ModePage0 : ModePage // page_0 mode page format 15 | { 16 | public bool PS; // Parameter Savable 17 | public bool SPF; // SubPage Format 18 | public ModePageCodeName PageCode; 19 | public byte PageLength; // excluding this and previous bytes 20 | 21 | protected ModePage0(ModePageCodeName pageCode, byte pageLength) 22 | { 23 | PageCode = pageCode; 24 | PageLength = pageLength; 25 | } 26 | 27 | public ModePage0(byte[] buffer, int offset) 28 | { 29 | PS = (buffer[offset + 0] & 0x80) != 0; 30 | SPF = (buffer[offset + 0] & 0x40) != 0; 31 | PageCode = (ModePageCodeName)(buffer[offset + 0] & 0x3F); 32 | PageLength = buffer[offset + 1]; 33 | } 34 | 35 | override public byte[] GetBytes() 36 | { 37 | byte[] buffer = new byte[2 + PageLength]; 38 | if (PS) 39 | { 40 | buffer[0] |= 0x80; 41 | } 42 | if (SPF) 43 | { 44 | buffer[0] |= 0x40; 45 | } 46 | buffer[0] |= (byte)((byte)PageCode & 0x3F); 47 | buffer[1] = PageLength; 48 | 49 | return buffer; 50 | } 51 | 52 | public override int Length 53 | { 54 | get 55 | { 56 | return 2 + PageLength; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ModePages/ModeParameterHeader10.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class ModeParameterHeader10 15 | { 16 | public const int Length = 8; 17 | 18 | public ushort ModeDataLength; // Excluding this field 19 | public byte MediumType; 20 | public bool WP; // Write Protect, indicates that the medium is write-protected 21 | public bool DPOFUA; // DPO and FUA support 22 | public bool LongLBA; 23 | public ushort BlockDescriptorLength; 24 | 25 | public ModeParameterHeader10() 26 | { 27 | ModeDataLength = 6; 28 | } 29 | 30 | public ModeParameterHeader10(byte[] buffer, int offset) 31 | { 32 | ModeDataLength = BigEndianConverter.ToUInt16(buffer, offset + 0); 33 | MediumType = ByteReader.ReadByte(buffer, offset + 2); 34 | WP = ((buffer[offset + 3] & 0x80) != 0); 35 | DPOFUA = ((buffer[offset + 3] & 0x10) != 0); 36 | LongLBA = ((buffer[offset + 4] & 0x01) != 0); 37 | BlockDescriptorLength = BigEndianConverter.ToUInt16(buffer, offset + 6); 38 | } 39 | 40 | public byte[] GetBytes() 41 | { 42 | byte[] buffer = new byte[Length]; 43 | BigEndianWriter.WriteUInt16(buffer, 0, ModeDataLength); 44 | ByteWriter.WriteByte(buffer, 2, MediumType); 45 | if (WP) 46 | { 47 | buffer[3] |= 0x80; 48 | } 49 | if (DPOFUA) 50 | { 51 | buffer[3] |= 0x10; 52 | } 53 | if (LongLBA) 54 | { 55 | buffer[4] |= 0x01; 56 | } 57 | BigEndianWriter.WriteUInt16(buffer, 6, BlockDescriptorLength); 58 | return buffer; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ModePages/ModeParameterHeader6.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2017 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class ModeParameterHeader6 15 | { 16 | public const int Length = 4; 17 | 18 | public byte ModeDataLength; // Excluding this byte 19 | public byte MediumType; 20 | public bool WP; // Write Protect, indicates that the medium is write-protected 21 | public bool DPOFUA; // DPO and FUA support 22 | public byte BlockDescriptorLength; 23 | 24 | public ModeParameterHeader6() 25 | { 26 | ModeDataLength = 3; 27 | } 28 | 29 | public ModeParameterHeader6(byte[] buffer, int offset) 30 | { 31 | ModeDataLength = buffer[offset + 0]; 32 | MediumType = buffer[offset + 1]; 33 | WP = ((buffer[offset + 2] & 0x80) != 0); 34 | DPOFUA = ((buffer[offset + 2] & 0x10) != 0); 35 | BlockDescriptorLength = buffer[offset + 3]; 36 | } 37 | 38 | public byte[] GetBytes() 39 | { 40 | byte[] buffer = new byte[Length]; 41 | buffer[0] = ModeDataLength; 42 | buffer[1] = MediumType; 43 | if (WP) 44 | { 45 | buffer[2] |= 0x80; 46 | } 47 | if (DPOFUA) 48 | { 49 | buffer[2] |= 0x10; 50 | } 51 | buffer[3] = BlockDescriptorLength; 52 | return buffer; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ModePages/PowerConditionModePage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class PowerConditionModePage : ModePage0 15 | { 16 | public byte PmBgPrecedence; 17 | public bool StandbyY; 18 | public bool IdleC; 19 | public bool IdleB; 20 | public bool IdleA; 21 | public bool StandbyZ; 22 | public uint IdleATimer; 23 | public uint StandbyZTimer; 24 | public uint IdleBTimer; 25 | public uint IdleCTimer; 26 | public uint StandbyYTimer; 27 | public byte CcfIdle; 28 | public byte CcfStandby; 29 | public byte CcfStopped; 30 | 31 | public PowerConditionModePage() : base(ModePageCodeName.PowerConditionModePage, 38) 32 | { } 33 | 34 | public PowerConditionModePage(byte[] buffer, int offset) : base(buffer, offset) 35 | { 36 | throw new NotImplementedException(); 37 | } 38 | 39 | public override byte[] GetBytes() 40 | { 41 | byte[] buffer = base.GetBytes(); 42 | buffer[2] = (byte)(PmBgPrecedence << 5 | (Convert.ToByte(StandbyY) & 0x01)); 43 | buffer[3] = (byte)((Convert.ToByte(IdleC) & 0x01) << 4 | (Convert.ToByte(IdleB) & 0x01) << 4 | (Convert.ToByte(IdleA) & 0x01) << 4 | (Convert.ToByte(StandbyZ) & 0x01)); 44 | BigEndianWriter.WriteUInt32(buffer, 4, IdleATimer); 45 | BigEndianWriter.WriteUInt32(buffer, 8, StandbyZTimer); 46 | BigEndianWriter.WriteUInt32(buffer, 12, IdleBTimer); 47 | BigEndianWriter.WriteUInt32(buffer, 16, IdleCTimer); 48 | BigEndianWriter.WriteUInt32(buffer, 20, StandbyYTimer); 49 | buffer[39] = (byte)((Convert.ToByte(CcfIdle) & 0x03) << 6 | (Convert.ToByte(CcfStandby) & 0x01) << 4 | (Convert.ToByte(CcfStopped) & 0x01) << 2); 50 | return buffer; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ModePages/PowerConsumptionModePage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class PowerConsumptionModePage : SubModePage 15 | { 16 | public byte PowerConsumptionIdentifier; 17 | 18 | public PowerConsumptionModePage() : base(ModePageCodeName.PowerConditionModePage, 0x01, 12) 19 | { 20 | } 21 | 22 | public PowerConsumptionModePage(byte[] buffer, int offset) : base(buffer, offset) 23 | { 24 | PowerConsumptionIdentifier = buffer[offset + 7]; 25 | } 26 | 27 | public override byte[] GetBytes() 28 | { 29 | byte[] buffer = base.GetBytes(); 30 | buffer[7] = PowerConsumptionIdentifier; 31 | 32 | return buffer; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ModePages/ShortLBAModeParameterBlockDescriptor.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2017 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class ShortLBAModeParameterBlockDescriptor 15 | { 16 | public const int Length = 8; 17 | 18 | public uint NumberOfBlocks; 19 | public byte Reserved; 20 | public uint LogicalBlockLength; // 3 bytes 21 | 22 | public ShortLBAModeParameterBlockDescriptor() 23 | { 24 | } 25 | 26 | public ShortLBAModeParameterBlockDescriptor(byte[] buffer, int offset) 27 | { 28 | NumberOfBlocks = BigEndianConverter.ToUInt32(buffer, offset + 0); 29 | Reserved = ByteReader.ReadByte(buffer, offset + 4); 30 | LogicalBlockLength = BigEndianReader.ReadUInt24(buffer, offset + 5); 31 | } 32 | 33 | public byte[] GetBytes() 34 | { 35 | byte[] buffer = new byte[Length]; 36 | BigEndianWriter.WriteUInt32(buffer, 0, NumberOfBlocks); 37 | ByteWriter.WriteByte(buffer, 4, Reserved); 38 | BigEndianWriter.WriteUInt24(buffer, 5, LogicalBlockLength); 39 | return buffer; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ModePages/SubModePage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class SubModePage : ModePage // SUB_PAGE mode page format 15 | { 16 | public bool PS; // Parameter Savable 17 | public bool SPF; // SubPage Format 18 | public ModePageCodeName PageCode; 19 | public byte SubPageCode; 20 | public ushort PageLength; // excluding this and previous bytes 21 | 22 | protected SubModePage(ModePageCodeName pageCode, byte subPageCode, ushort pageLength) 23 | { 24 | PageCode = pageCode; 25 | PageLength = pageLength; 26 | } 27 | 28 | public SubModePage(byte[] buffer, int offset) 29 | { 30 | PS = (buffer[offset + 0] & 0x80) != 0; 31 | SPF = (buffer[offset + 0] & 0x40) != 0; 32 | PageCode = (ModePageCodeName)(buffer[offset + 0] & 0x3F); 33 | SubPageCode = buffer[offset + 1]; 34 | PageLength = BigEndianConverter.ToUInt16(buffer, 2); 35 | } 36 | 37 | override public byte[] GetBytes() 38 | { 39 | byte[] buffer = new byte[4 + PageLength]; 40 | if (PS) 41 | { 42 | buffer[0] |= 0x80; 43 | } 44 | if (SPF) 45 | { 46 | buffer[0] |= 0x40; 47 | } 48 | buffer[0] |= (byte)((byte)PageCode & 0x3F); 49 | buffer[1] = SubPageCode; 50 | BigEndianWriter.WriteUInt16(buffer, 2, PageLength); 51 | 52 | return buffer; 53 | } 54 | 55 | public override int Length 56 | { 57 | get 58 | { 59 | return 4 + PageLength; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ModePages/VendorSpecificPage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class VendorSpecificPage : ModePage 15 | { 16 | public VendorSpecificPage() 17 | { 18 | } 19 | 20 | public VendorSpecificPage(byte[] buffer, int offset) 21 | { 22 | } 23 | 24 | public override byte[] GetBytes() 25 | { 26 | byte[] buffer = new byte[0]; 27 | return buffer; 28 | } 29 | 30 | // Note: Vendor specific page does not require page format 31 | public override int Length 32 | { 33 | get 34 | { 35 | return 0; 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ReadCapacity10Parameter.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class ReadCapacity10Parameter 15 | { 16 | public const int Length = 8; 17 | 18 | public uint ReturnedLBA; // the LBA of the last logical block on the direct-access block device 19 | public uint BlockLengthInBytes; // block size 20 | 21 | public ReadCapacity10Parameter() 22 | { 23 | } 24 | 25 | public ReadCapacity10Parameter(byte[] buffer) 26 | { 27 | ReturnedLBA = BigEndianConverter.ToUInt32(buffer, 0); 28 | BlockLengthInBytes = BigEndianConverter.ToUInt32(buffer, 4); 29 | } 30 | 31 | public ReadCapacity10Parameter(long diskSize, uint blockSizeInBytes) 32 | { 33 | // If the number of logical blocks exceeds the maximum value that is able to be specified in the RETURNED LOGICAL BLOCK ADDRESS field, 34 | // the device server shall set the RETURNED LOGICAL BLOCK ADDRESS field to 0xFFFFFFFF 35 | long returnedLBA = diskSize / blockSizeInBytes - 1; // zero-based LBA of the last logical block 36 | if (returnedLBA <= UInt32.MaxValue) 37 | { 38 | ReturnedLBA = (uint)returnedLBA; 39 | } 40 | else 41 | { 42 | ReturnedLBA = 0xFFFFFFFF; 43 | } 44 | 45 | BlockLengthInBytes = blockSizeInBytes; 46 | } 47 | 48 | public byte[] GetBytes() 49 | { 50 | byte[] buffer = new byte[Length]; 51 | BigEndianWriter.WriteUInt32(buffer, 0, ReturnedLBA); 52 | BigEndianWriter.WriteUInt32(buffer, 4, BlockLengthInBytes); 53 | return buffer; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ReadCapacity16Parameter.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class ReadCapacity16Parameter 15 | { 16 | public const int Length = 32; 17 | 18 | public ulong ReturnedLBA; // the LBA of the last logical block on the direct-access block device 19 | public uint BlockLengthInBytes; // block size 20 | 21 | public ReadCapacity16Parameter() 22 | { 23 | } 24 | 25 | public ReadCapacity16Parameter(byte[] buffer) 26 | { 27 | ReturnedLBA = BigEndianConverter.ToUInt64(buffer, 0); 28 | BlockLengthInBytes = BigEndianConverter.ToUInt32(buffer, 8); 29 | } 30 | 31 | public ReadCapacity16Parameter(long diskSize, uint blockSizeInBytes) 32 | { 33 | ReturnedLBA = (ulong)diskSize / blockSizeInBytes - 1; // zero-based LBA of the last logical block 34 | BlockLengthInBytes = blockSizeInBytes; 35 | } 36 | 37 | public byte[] GetBytes() 38 | { 39 | byte[] buffer = new byte[Length]; 40 | BigEndianWriter.WriteUInt64(buffer, 0, ReturnedLBA); 41 | BigEndianWriter.WriteUInt32(buffer, 8, BlockLengthInBytes); 42 | return buffer; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/ReportLUNsParameter.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class ReportLUNsParameter 15 | { 16 | /// 17 | /// Minimum allocation length defined by the REPORT LUNS command 18 | /// 19 | public const int MinimumAllocationLength = 16; 20 | 21 | public List LUNList = new List(); 22 | 23 | public ReportLUNsParameter() 24 | { 25 | } 26 | 27 | public ReportLUNsParameter(int numberOfLUNs) 28 | { 29 | if (numberOfLUNs > LUNStructure.SingleLevelAddressingLimit) 30 | { 31 | throw new NotImplementedException("Unsupported Number of LUNs"); 32 | } 33 | 34 | for (int index = 0; index < numberOfLUNs; index++) 35 | { 36 | LUNList.Add((ushort)index); 37 | } 38 | } 39 | 40 | public ReportLUNsParameter(byte[] buffer) 41 | { 42 | uint listLength = BigEndianConverter.ToUInt32(buffer, 0); 43 | // uint reserved = BigEndianConverter.ToUInt32(buffer, 4); 44 | int offset = 8; 45 | int lunCount = (int)(listLength / 8); 46 | for (int index = 0; index < lunCount; index++) 47 | { 48 | LUNStructure structure = new LUNStructure(buffer, offset); 49 | LUNList.Add(structure); 50 | offset += 8; 51 | } 52 | } 53 | 54 | public byte[] GetBytes() 55 | { 56 | uint LUNListLength = (uint)LUNList.Count * 8; 57 | byte[] buffer = new byte[8 + LUNListLength]; 58 | BigEndianWriter.WriteUInt32(buffer, 0, LUNListLength); 59 | int offset = 8; 60 | for (int index = 0; index < LUNList.Count; index++) 61 | { 62 | byte[] structureBytes = LUNList[index].GetBytes(); 63 | ByteWriter.WriteBytes(buffer, offset, structureBytes); 64 | offset += 8; 65 | } 66 | return buffer; 67 | } 68 | 69 | public static uint GetRequiredAllocationLength(byte[] buffer) 70 | { 71 | uint listLength = BigEndianConverter.ToUInt32(buffer, 0); 72 | return 8 + listLength; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/VPDPages/BlockDeviceCharacteristicsVPDPage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2017 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class BlockDeviceCharacteristicsVPDPage 15 | { 16 | public byte PeripheralQualifier; 17 | public PeripheralDeviceType PeripheralDeviceType; 18 | public VitalProductDataPageName PageCode; 19 | public byte PageLength; 20 | public ushort MediumRotationRate; 21 | public ushort Reserved; 22 | 23 | public BlockDeviceCharacteristicsVPDPage() 24 | { 25 | PageCode = VitalProductDataPageName.BlockDeviceCharacteristics; 26 | PageLength = 4; 27 | } 28 | 29 | public BlockDeviceCharacteristicsVPDPage(byte[] buffer, int offset) 30 | { 31 | PeripheralQualifier = (byte)(buffer[offset + 0] >> 5); 32 | PeripheralDeviceType = (PeripheralDeviceType)(buffer[offset + 0] & 0x1F); 33 | PageCode = (VitalProductDataPageName)buffer[offset + 1]; 34 | PageLength = buffer[offset + 3]; 35 | MediumRotationRate = BigEndianConverter.ToUInt16(buffer, offset + 4); 36 | Reserved = BigEndianConverter.ToUInt16(buffer, offset + 6); 37 | } 38 | 39 | public byte[] GetBytes() 40 | { 41 | byte[] buffer = new byte[4 + PageLength]; 42 | buffer[0] |= (byte)(PeripheralQualifier << 5); 43 | buffer[0] |= (byte)(PeripheralQualifier & 0x1F); 44 | buffer[1] = (byte)PageCode; 45 | buffer[3] = PageLength; 46 | BigEndianWriter.WriteUInt16(buffer, 4, MediumRotationRate); 47 | BigEndianWriter.WriteUInt16(buffer, 6, Reserved); 48 | return buffer; 49 | } 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/VPDPages/BlockLimitsVPDPage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2017 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class BlockLimitsVPDPage 15 | { 16 | public byte PeripheralQualifier; 17 | public PeripheralDeviceType PeripheralDeviceType; 18 | public VitalProductDataPageName PageCode; // VitalProductDataPageName 19 | public byte PageLength; 20 | public ushort OptimalTransferLengthGranularity; 21 | public uint MaximumTransferLength; 22 | public uint OptimalTransferLength; 23 | 24 | public BlockLimitsVPDPage() 25 | { 26 | PageCode = VitalProductDataPageName.BlockLimits; 27 | PageLength = 12; 28 | } 29 | 30 | public BlockLimitsVPDPage(byte[] buffer, int offset) 31 | { 32 | PeripheralQualifier = (byte)(buffer[offset + 0] >> 5); 33 | PeripheralDeviceType = (PeripheralDeviceType)(buffer[offset + 0] & 0x1F); 34 | PageCode = (VitalProductDataPageName)buffer[offset + 1]; 35 | PageLength = buffer[offset + 3]; 36 | OptimalTransferLengthGranularity = BigEndianConverter.ToUInt16(buffer, offset + 6); 37 | MaximumTransferLength = BigEndianConverter.ToUInt32(buffer, offset + 8); 38 | OptimalTransferLength = BigEndianConverter.ToUInt32(buffer, offset + 12); 39 | } 40 | 41 | public byte[] GetBytes() 42 | { 43 | byte[] buffer = new byte[4 + PageLength]; 44 | buffer[0] |= (byte)(PeripheralQualifier << 5); 45 | buffer[0] |= (byte)(PeripheralQualifier & 0x1F); 46 | buffer[1] = (byte)PageCode; 47 | buffer[3] = PageLength; 48 | BigEndianWriter.WriteUInt16(buffer, 6, OptimalTransferLengthGranularity); 49 | BigEndianWriter.WriteUInt32(buffer, 8, MaximumTransferLength); 50 | BigEndianWriter.WriteUInt32(buffer, 12, OptimalTransferLength); 51 | return buffer; 52 | } 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/VPDPages/DeviceIdentificationVPDPage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2017 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class DeviceIdentificationVPDPage 15 | { 16 | public byte PeripheralQualifier; 17 | public PeripheralDeviceType PeripheralDeviceType; 18 | public VitalProductDataPageName PageCode; // VitalProductDataPageName 19 | public ushort PageLength; 20 | public List IdentificationDescriptorList = new List(); 21 | 22 | public DeviceIdentificationVPDPage() 23 | { 24 | PageCode = VitalProductDataPageName.DeviceIdentification; 25 | } 26 | 27 | public DeviceIdentificationVPDPage(byte[] buffer, int offset) 28 | { 29 | PeripheralQualifier = (byte)(buffer[offset + 0] >> 5); 30 | PeripheralDeviceType = (PeripheralDeviceType)(buffer[offset + 0] & 0x1F); 31 | PageCode = (VitalProductDataPageName)buffer[offset + 1]; 32 | PageLength = BigEndianConverter.ToUInt16(buffer, 2); 33 | int parameterOffset = 4; 34 | while (parameterOffset < PageLength) 35 | { 36 | IdentificationDescriptor descriptor = new IdentificationDescriptor(buffer, offset + parameterOffset); 37 | IdentificationDescriptorList.Add(descriptor); 38 | parameterOffset += descriptor.Length; 39 | } 40 | } 41 | 42 | public byte[] GetBytes() 43 | { 44 | PageLength = 0; 45 | foreach(IdentificationDescriptor descriptor in IdentificationDescriptorList) 46 | { 47 | PageLength += (ushort)descriptor.Length; 48 | } 49 | 50 | byte[] buffer = new byte[4 + PageLength]; 51 | buffer[0] |= (byte)(PeripheralQualifier << 5); 52 | buffer[0] |= (byte)(PeripheralQualifier & 0x1F); 53 | buffer[1] = (byte)PageCode; 54 | BigEndianWriter.WriteUInt16(buffer, 2, PageLength); 55 | 56 | int offset = 4; 57 | foreach (IdentificationDescriptor descriptor in IdentificationDescriptorList) 58 | { 59 | Array.Copy(descriptor.GetBytes(), 0, buffer, offset, descriptor.Length); 60 | offset += descriptor.Length; 61 | } 62 | return buffer; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/VPDPages/Enums/IdentifierTypeName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SCSI 3 | { 4 | public enum IdentifierTypeName : byte 5 | { 6 | VendorSpecific = 0x00, 7 | T10 = 0x01, // T10 vendor identification 8 | EUI64 = 0x02, // EUI-64 9 | NAA = 0x03, 10 | RelativeTargetPort = 0x04, 11 | TargetPortGroup = 0x05, 12 | LogicalUnitGroup = 0x06, 13 | MD5LogicalUnitIdentifier = 0x07, 14 | ScsiNameString = 0x08, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/VPDPages/SupportedVitaLProductDataPages.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2017 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class SupportedVitaLProductDataPages 15 | { 16 | public byte PeripheralQualifier; 17 | public PeripheralDeviceType PeripheralDeviceType; 18 | public byte PageLength; 19 | public List SupportedPageList = new List(); 20 | 21 | public SupportedVitaLProductDataPages() 22 | { 23 | } 24 | 25 | public SupportedVitaLProductDataPages(byte[] buffer, int offset) 26 | { 27 | PeripheralQualifier = (byte)(buffer[offset + 0] >> 5); 28 | PeripheralDeviceType = (PeripheralDeviceType)(buffer[offset + 0] & 0x1F); 29 | PageLength = buffer[offset + 3]; 30 | for (int index = 0; index < PageLength; index++) 31 | { 32 | SupportedPageList.Add(buffer[offset + 4 + index]); 33 | } 34 | } 35 | 36 | public byte[] GetBytes() 37 | { 38 | byte[] buffer = new byte[4 + SupportedPageList.Count]; 39 | buffer[0] |= (byte)(PeripheralQualifier << 5); 40 | buffer[0] |= (byte)(PeripheralQualifier & 0x1F); 41 | buffer[3] = (byte)SupportedPageList.Count; 42 | SupportedPageList.Sort(); // must be sorted by ascending order 43 | SupportedPageList.CopyTo(buffer, 4); 44 | return buffer; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ISCSI/SCSI/SCSIReturnParameters/VPDPages/UnitSerialNumberVPDPage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2017 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Utilities; 11 | 12 | namespace SCSI 13 | { 14 | public class UnitSerialNumberVPDPage 15 | { 16 | public byte PeripheralQualifier; 17 | public PeripheralDeviceType PeripheralDeviceType; 18 | public VitalProductDataPageName PageCode; // VitalProductDataPageName 19 | public byte PageLength; 20 | public string ProductSerialNumber; 21 | 22 | public UnitSerialNumberVPDPage() 23 | { 24 | PageCode = VitalProductDataPageName.UnitSerialNumber; 25 | } 26 | 27 | public UnitSerialNumberVPDPage(byte[] buffer, int offset) 28 | { 29 | PeripheralQualifier = (byte)(buffer[offset + 0] >> 5); 30 | PeripheralDeviceType = (PeripheralDeviceType)(buffer[offset + 0] & 0x1F); 31 | PageCode = (VitalProductDataPageName)buffer[offset + 1]; 32 | PageLength = buffer[offset + 3]; 33 | ProductSerialNumber = ASCIIEncoding.ASCII.GetString(buffer, offset + 4, PageLength); 34 | } 35 | 36 | public byte[] GetBytes() 37 | { 38 | byte[] buffer = new byte[4 + ProductSerialNumber.Length]; 39 | buffer[0] |= (byte)(PeripheralQualifier << 5); 40 | buffer[0] |= (byte)(PeripheralQualifier & 0x1F); 41 | buffer[1] = (byte)PageCode; 42 | buffer[3] = (byte)ProductSerialNumber.Length; 43 | ByteWriter.WriteAnsiString(buffer, 4, ProductSerialNumber); 44 | return buffer; 45 | } 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ISCSI/SCSITarget/InquiryEventArgs/DeviceIdentificationInquiryEventArgs.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace SCSI 12 | { 13 | public class DeviceIdentificationInquiryEventArgs : EventArgs 14 | { 15 | public LUNStructure LUN; 16 | /// 17 | /// Can be altered by the event subscriber 18 | /// 19 | public DeviceIdentificationVPDPage Page; 20 | 21 | public DeviceIdentificationInquiryEventArgs(LUNStructure lun, DeviceIdentificationVPDPage page) 22 | { 23 | LUN = lun; 24 | Page = page; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ISCSI/SCSITarget/InquiryEventArgs/StandardInquiryEventArgs.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace SCSI 12 | { 13 | public class StandardInquiryEventArgs : EventArgs 14 | { 15 | public LUNStructure LUN; 16 | /// 17 | /// Can be altered by the event subscriber 18 | /// 19 | public StandardInquiryData Data; 20 | 21 | public StandardInquiryEventArgs(LUNStructure lun, StandardInquiryData data) 22 | { 23 | LUN = lun; 24 | Data = data; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ISCSI/SCSITarget/InquiryEventArgs/UnitSerialNumberInquiryEventArgs.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace SCSI 12 | { 13 | public class UnitSerialNumberInquiryEventArgs : EventArgs 14 | { 15 | public LUNStructure LUN; 16 | /// 17 | /// Can be altered by the event subscriber 18 | /// 19 | public UnitSerialNumberVPDPage Page; 20 | 21 | public UnitSerialNumberInquiryEventArgs(LUNStructure lun, UnitSerialNumberVPDPage page) 22 | { 23 | LUN = lun; 24 | Page = page; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ISCSI/SCSITarget/SCSITarget.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using System.Threading; 11 | using Utilities; 12 | 13 | namespace SCSI 14 | { 15 | public abstract class SCSITarget : SCSITargetInterface 16 | { 17 | private class SCSICommand 18 | { 19 | public byte[] CommandBytes; 20 | public LUNStructure LUN; 21 | public byte[] Data; 22 | public object Task; 23 | public OnCommandCompleted OnCommandCompleted; 24 | } 25 | 26 | private BlockingQueue m_commandQueue = new BlockingQueue(); 27 | 28 | public event EventHandler OnStandardInquiry; 29 | 30 | public event EventHandler OnUnitSerialNumberInquiry; 31 | 32 | public event EventHandler OnDeviceIdentificationInquiry; 33 | 34 | public SCSITarget() 35 | { 36 | Thread workerThread = new Thread(ProcessCommandQueue); 37 | workerThread.IsBackground = true; 38 | workerThread.Start(); 39 | } 40 | 41 | private void ProcessCommandQueue() 42 | { 43 | while (true) 44 | { 45 | SCSICommand command; 46 | bool stopping = !m_commandQueue.TryDequeue(out command); 47 | if (stopping) 48 | { 49 | return; 50 | } 51 | 52 | byte[] responseBytes; 53 | SCSIStatusCodeName status = ExecuteCommand(command.CommandBytes, command.LUN, command.Data, out responseBytes); 54 | command.OnCommandCompleted(status, responseBytes, command.Task); 55 | } 56 | } 57 | 58 | public void QueueCommand(byte[] commandBytes, LUNStructure lun, byte[] data, object task, OnCommandCompleted OnCommandCompleted) 59 | { 60 | SCSICommand command = new SCSICommand(); 61 | command.CommandBytes = commandBytes; 62 | command.LUN = lun; 63 | command.Data = data; 64 | command.OnCommandCompleted = OnCommandCompleted; 65 | command.Task = task; 66 | m_commandQueue.Enqueue(command); 67 | } 68 | 69 | public abstract SCSIStatusCodeName ExecuteCommand(byte[] commandBytes, LUNStructure lun, byte[] data, out byte[] response); 70 | 71 | protected void NotifyStandardInquiry(object sender, StandardInquiryEventArgs args) 72 | { 73 | // To be thread-safe we must capture the delegate reference first 74 | EventHandler handler = OnStandardInquiry; 75 | if (handler != null) 76 | { 77 | handler(sender, args); 78 | } 79 | } 80 | 81 | protected void NotifyUnitSerialNumberInquiry(object sender, UnitSerialNumberInquiryEventArgs args) 82 | { 83 | // To be thread-safe we must capture the delegate reference first 84 | EventHandler handler = OnUnitSerialNumberInquiry; 85 | if (handler != null) 86 | { 87 | handler(sender, args); 88 | } 89 | } 90 | 91 | protected void NotifyDeviceIdentificationInquiry(object sender, DeviceIdentificationInquiryEventArgs args) 92 | { 93 | // To be thread-safe we must capture the delegate reference first 94 | EventHandler handler = OnDeviceIdentificationInquiry; 95 | if (handler != null) 96 | { 97 | handler(sender, args); 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /ISCSI/SCSITarget/SCSITargetInterface.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace SCSI 12 | { 13 | public delegate void OnCommandCompleted(SCSIStatusCodeName status, byte[] responseBytes, object task); 14 | 15 | public interface SCSITargetInterface 16 | { 17 | event EventHandler OnStandardInquiry; 18 | 19 | event EventHandler OnUnitSerialNumberInquiry; 20 | 21 | event EventHandler OnDeviceIdentificationInquiry; 22 | 23 | void QueueCommand(byte[] commandBytes, LUNStructure lun, byte[] data, object task, OnCommandCompleted OnCommandCompleted); 24 | 25 | SCSIStatusCodeName ExecuteCommand(byte[] commandBytes, LUNStructure lun, byte[] data, out byte[] response); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ISCSI/Utilities/KeyValuePairUtils.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2024 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | using System.Text; 9 | 10 | namespace Utilities 11 | { 12 | internal class KeyValuePairUtils 13 | { 14 | public static KeyValuePairList GetKeyValuePairList(string nullDelimitedString) 15 | { 16 | KeyValuePairList result = new KeyValuePairList(); 17 | 18 | string[] entries = nullDelimitedString.Split('\0'); 19 | foreach (string entry in entries) 20 | { 21 | string[] pair = entry.Split('='); 22 | if (pair.Length >= 2) 23 | { 24 | string key = pair[0]; 25 | string value = pair[1]; 26 | result.Add(key, value); 27 | } 28 | } 29 | return result; 30 | } 31 | 32 | public static string ToNullDelimitedString(List> list) 33 | { 34 | StringBuilder builder = new StringBuilder(); 35 | foreach (KeyValuePair pair in list) 36 | { 37 | builder.AppendFormat("{0}={1}\0", pair.Key, pair.Value); 38 | } 39 | return builder.ToString(); 40 | } 41 | 42 | public static string ToString(KeyValuePairList list) 43 | { 44 | StringBuilder builder = new StringBuilder(); 45 | for (int index = 0; index < list.Count; index++) 46 | { 47 | if (index > 0) 48 | { 49 | builder.Append(", "); 50 | } 51 | builder.AppendFormat("{0}={1}", list[index].Key, list[index].Value); 52 | } 53 | return builder.ToString(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ISCSI/Utilities/SocketUtils.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2024 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Net.Sockets; 9 | 10 | namespace Utilities 11 | { 12 | internal class SocketUtils 13 | { 14 | public static void SetKeepAlive(Socket socket, TimeSpan timeout) 15 | { 16 | // The default settings when a TCP socket is initialized sets the keep-alive timeout to 2 hours and the keep-alive interval to 1 second. 17 | SetKeepAlive(socket, true, timeout, TimeSpan.FromSeconds(1)); 18 | } 19 | 20 | /// the timeout, in milliseconds, with no activity until the first keep-alive packet is sent 21 | /// the interval, in milliseconds, between when successive keep-alive packets are sent if no acknowledgement is received 22 | public static void SetKeepAlive(Socket socket, bool enable, TimeSpan timeout, TimeSpan interval) 23 | { 24 | socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); 25 | // https://msdn.microsoft.com/en-us/library/dd877220.aspx 26 | byte[] tcp_keepalive = new byte[12]; 27 | LittleEndianWriter.WriteUInt32(tcp_keepalive, 0, Convert.ToUInt32(enable)); 28 | LittleEndianWriter.WriteUInt32(tcp_keepalive, 4, (uint)timeout.TotalMilliseconds); 29 | LittleEndianWriter.WriteUInt32(tcp_keepalive, 8, (uint)interval.TotalMilliseconds); 30 | socket.IOControl(IOControlCode.KeepAliveValues, tcp_keepalive, null); 31 | } 32 | 33 | /// 34 | /// Socket will be forcefully closed, all pending data will be ignored, and socket will be deallocated. 35 | /// 36 | public static void ReleaseSocket(Socket socket) 37 | { 38 | if (socket != null) 39 | { 40 | if (socket.Connected) 41 | { 42 | try 43 | { 44 | socket.Shutdown(SocketShutdown.Both); 45 | socket.Disconnect(false); 46 | } 47 | catch (ObjectDisposedException) 48 | { 49 | return; 50 | } 51 | catch (SocketException) 52 | { } 53 | } 54 | socket.Close(); 55 | socket = null; 56 | GC.Collect(); 57 | GC.WaitForPendingFinalizers(); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ISCSI/Win32/SCSI/Enums/SCSIDataDirection.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SCSI.Win32 3 | { 4 | public enum SCSIDataDirection : byte 5 | { 6 | Out = 0x0, 7 | In = 0x1, 8 | NoData = 0x2, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ISCSI/Win32/SCSI/Structures/SCSI_ADAPTER_BUS_INFO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using Utilities; 5 | 6 | namespace SCSI.Win32 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public class SCSI_ADAPTER_BUS_INFO 10 | { 11 | public byte NumberOfBuses; 12 | public SCSI_BUS_DATA[] BusData; 13 | 14 | public SCSI_ADAPTER_BUS_INFO() 15 | { 16 | } 17 | 18 | public static SCSI_ADAPTER_BUS_INFO FromIntPtr(IntPtr ptr) 19 | { 20 | SCSI_ADAPTER_BUS_INFO busInfo = new SCSI_ADAPTER_BUS_INFO(); 21 | byte numberOfBuses = Marshal.ReadByte(ptr); 22 | ptr = new IntPtr(ptr.ToInt64() + 4); 23 | busInfo.NumberOfBuses = numberOfBuses; 24 | busInfo.BusData = new SCSI_BUS_DATA[numberOfBuses]; 25 | for (int index = 0; index < numberOfBuses; index++) 26 | { 27 | busInfo.BusData[index] = (SCSI_BUS_DATA)Marshal.PtrToStructure(ptr, typeof(SCSI_BUS_DATA)); 28 | ptr = new IntPtr(ptr.ToInt64() + Marshal.SizeOf(typeof(SCSI_BUS_DATA))); 29 | } 30 | return busInfo; 31 | } 32 | 33 | public static List GetInquiryDataForAllDevices(IntPtr busInfoPtr) 34 | { 35 | SCSI_ADAPTER_BUS_INFO busInfo = FromIntPtr(busInfoPtr); 36 | List devices = new List(); 37 | foreach (SCSI_BUS_DATA busData in busInfo.BusData) 38 | { 39 | byte numberOfLuns = busData.NumberOfLogicalUnits; 40 | uint inquiryDataOffset = busData.InquiryDataOffset; 41 | for (int lunIndex = 0; lunIndex < numberOfLuns; lunIndex++) 42 | { 43 | IntPtr inquiryDataPtr = new IntPtr(busInfoPtr.ToInt64() + inquiryDataOffset); 44 | SCSI_INQUIRY_DATA inquiryData = SCSI_INQUIRY_DATA.FromIntPtr(inquiryDataPtr); 45 | devices.Add(inquiryData); 46 | inquiryDataOffset = inquiryData.NextInquiryDataOffset; 47 | } 48 | } 49 | return devices; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ISCSI/Win32/SCSI/Structures/SCSI_BUS_DATA.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace SCSI.Win32 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | public class SCSI_BUS_DATA 8 | { 9 | public byte NumberOfLogicalUnits; 10 | public byte InitiatorBusId; 11 | public uint InquiryDataOffset; 12 | 13 | public SCSI_BUS_DATA() 14 | { 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ISCSI/Win32/SCSI/Structures/SCSI_INQUIRY_DATA.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace SCSI.Win32 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | public class SCSI_INQUIRY_DATA 8 | { 9 | public byte PathId; 10 | public byte TargetId; 11 | public byte Lun; 12 | public bool DeviceClaimed; // Indicates that the device has been claimed by a class driver. 13 | public uint InquiryDataLength; 14 | public uint NextInquiryDataOffset; 15 | public byte[] InquiryData; 16 | 17 | public SCSI_INQUIRY_DATA() 18 | { 19 | } 20 | 21 | public static SCSI_INQUIRY_DATA FromIntPtr(IntPtr ptr) 22 | { 23 | SCSI_INQUIRY_DATA inquiryData = new SCSI_INQUIRY_DATA(); 24 | inquiryData.PathId = Marshal.ReadByte(ptr, 0); 25 | inquiryData.TargetId = Marshal.ReadByte(ptr, 1); 26 | inquiryData.Lun = Marshal.ReadByte(ptr, 2); 27 | inquiryData.DeviceClaimed = Convert.ToBoolean(Marshal.ReadByte(ptr, 3)); 28 | inquiryData.InquiryDataLength = (uint)Marshal.ReadInt32(ptr, 4); 29 | inquiryData.NextInquiryDataOffset = (uint)Marshal.ReadInt32(ptr, 8); 30 | inquiryData.InquiryData = new byte[inquiryData.InquiryDataLength]; 31 | int inquiryDataOffset = 12; 32 | IntPtr inquiryDataPtr = new IntPtr(ptr.ToInt64() + inquiryDataOffset); 33 | Marshal.Copy(inquiryDataPtr, inquiryData.InquiryData, 0, inquiryData.InquiryData.Length); 34 | return inquiryData; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ISCSI/Win32/SCSI/Structures/SCSI_PASS_THROUGH_DIRECT.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace SCSI.Win32 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | public class SCSI_PASS_THROUGH_DIRECT 8 | { 9 | public const int CdbBufferLength = 16; 10 | 11 | public ushort Length; 12 | public byte ScsiStatus; 13 | public byte PathId; 14 | public byte TargetId; 15 | public byte Lun; 16 | public byte CdbLength; 17 | public byte SenseInfoLength; 18 | public byte DataIn; 19 | public uint DataTransferLength; 20 | public uint TimeOutValue; 21 | public IntPtr DataBuffer; 22 | public uint SenseInfoOffset; 23 | 24 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = CdbBufferLength)] 25 | public byte[] Cdb; 26 | 27 | public SCSI_PASS_THROUGH_DIRECT() 28 | { 29 | Cdb = new byte[CdbBufferLength]; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ISCSI/Win32/SCSI/Structures/SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace SCSI.Win32 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | public class SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER 8 | { 9 | public const int SenseBufferLength = 32; 10 | 11 | public SCSI_PASS_THROUGH_DIRECT Spt = new SCSI_PASS_THROUGH_DIRECT(); 12 | 13 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = SenseBufferLength)] 14 | public byte[] Sense; 15 | 16 | public SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER() 17 | { 18 | Sense = new byte[SenseBufferLength]; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ISCSI/Win32/SCSITarget/LogicalUnitManager.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 Tal Aloni . 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace SCSI.Win32 11 | { 12 | internal class LogicalUnit 13 | { 14 | public byte AssociatedLun; 15 | public byte PathId; 16 | public byte TargetId; 17 | public byte TargetLun; 18 | public PeripheralDeviceType DeviceType; 19 | public uint? BlockSize; 20 | } 21 | 22 | internal class LogicalUnitManager 23 | { 24 | private IDictionary m_luns = new Dictionary(); 25 | 26 | public LogicalUnitManager() 27 | { 28 | } 29 | 30 | public void AddLogicalUnit(LogicalUnit logicalUnit) 31 | { 32 | m_luns.Add(logicalUnit.AssociatedLun, logicalUnit); 33 | } 34 | 35 | public LogicalUnit FindLogicalUnit(byte lun) 36 | { 37 | LogicalUnit result; 38 | m_luns.TryGetValue(lun, out result); 39 | return result; 40 | } 41 | 42 | public byte? FindAssociatedLUN(byte pathId, byte targetId, byte targetLun) 43 | { 44 | foreach (byte associatedLUN in m_luns.Keys) 45 | { 46 | if (m_luns[associatedLUN].PathId == pathId && 47 | m_luns[associatedLUN].TargetId == targetId && 48 | m_luns[associatedLUN].TargetLun == targetLun) 49 | { 50 | return associatedLUN; 51 | } 52 | } 53 | return null; 54 | } 55 | 56 | public byte? FindUnusedLUN() 57 | { 58 | // Windows supports 0x00 - 0xFE 59 | for (byte lun = 0; lun < 255; lun++) 60 | { 61 | if (!m_luns.ContainsKey(lun)) 62 | { 63 | return lun; 64 | } 65 | } 66 | return null; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /ISCSIConsole.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30717.126 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ISCSIConsole", "ISCSIConsole\ISCSIConsole.csproj", "{6C9727E4-00AC-4DE0-86A3-5A10259DE3E4}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ISCSI", "ISCSI\ISCSI.csproj", "{225ABFC2-FCA5-4C23-B7E3-0874E9A60548}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utilities", "Utilities\Utilities.csproj", "{6E0F2D1E-6167-4032-BA90-DEE3A99207D0}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {6C9727E4-00AC-4DE0-86A3-5A10259DE3E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {6C9727E4-00AC-4DE0-86A3-5A10259DE3E4}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {6C9727E4-00AC-4DE0-86A3-5A10259DE3E4}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {6C9727E4-00AC-4DE0-86A3-5A10259DE3E4}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {225ABFC2-FCA5-4C23-B7E3-0874E9A60548}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {225ABFC2-FCA5-4C23-B7E3-0874E9A60548}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {225ABFC2-FCA5-4C23-B7E3-0874E9A60548}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {225ABFC2-FCA5-4C23-B7E3-0874E9A60548}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {6E0F2D1E-6167-4032-BA90-DEE3A99207D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {6E0F2D1E-6167-4032-BA90-DEE3A99207D0}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {6E0F2D1E-6167-4032-BA90-DEE3A99207D0}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {6E0F2D1E-6167-4032-BA90-DEE3A99207D0}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {BF0B3541-4864-4A9E-AD68-DE84CCB953B5} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /ISCSIConsole/CreateRAMDiskForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Drawing; 5 | using System.IO; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Windows.Forms; 9 | using DiskAccessLibrary; 10 | 11 | namespace ISCSIConsole 12 | { 13 | public partial class CreateRAMDiskForm : Form 14 | { 15 | private RAMDisk m_ramDisk; 16 | 17 | public CreateRAMDiskForm() 18 | { 19 | InitializeComponent(); 20 | } 21 | 22 | private void btnCancel_Click(object sender, EventArgs e) 23 | { 24 | this.DialogResult = DialogResult.Cancel; 25 | this.Close(); 26 | } 27 | 28 | private void btnOK_Click(object sender, EventArgs e) 29 | { 30 | int size = (int)numericDiskSize.Value * 1024 * 1024; 31 | RAMDisk disk; 32 | try 33 | { 34 | disk = new RAMDisk(size); 35 | } 36 | catch (OutOfMemoryException) 37 | { 38 | MessageBox.Show("Not enough memory available", "Error"); 39 | return; 40 | } 41 | m_ramDisk = disk; 42 | this.DialogResult = DialogResult.OK; 43 | this.Close(); 44 | } 45 | 46 | public RAMDisk RAMDisk 47 | { 48 | get 49 | { 50 | return m_ramDisk; 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /ISCSIConsole/Disks/VolumeDisk.cs: -------------------------------------------------------------------------------- 1 | using DiskAccessLibrary; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Utilities; 6 | 7 | namespace ISCSIConsole 8 | { 9 | public class VolumeDisk : Disk // a fake disk that serves a single volume 10 | { 11 | private Volume m_volume; 12 | private bool m_isReadOnly; 13 | 14 | public VolumeDisk(Volume volume, bool isReadOnly) 15 | { 16 | m_volume = volume; 17 | m_isReadOnly = volume.IsReadOnly || isReadOnly; 18 | } 19 | 20 | public override byte[] ReadSectors(long sectorIndex, int sectorCount) 21 | { 22 | return m_volume.ReadSectors(sectorIndex, sectorCount); 23 | } 24 | 25 | public override void WriteSectors(long sectorIndex, byte[] data) 26 | { 27 | if (!IsReadOnly) 28 | { 29 | m_volume.WriteSectors(sectorIndex, data); 30 | } 31 | } 32 | 33 | public override int BytesPerSector 34 | { 35 | get 36 | { 37 | return m_volume.BytesPerSector; 38 | } 39 | } 40 | 41 | public override long Size 42 | { 43 | get 44 | { 45 | return m_volume.Size; 46 | } 47 | } 48 | 49 | public override bool IsReadOnly 50 | { 51 | get 52 | { 53 | return m_isReadOnly; 54 | } 55 | } 56 | 57 | public Volume Volume 58 | { 59 | get 60 | { 61 | return m_volume; 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ISCSIConsole/Helpers/FormattingHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace ISCSIConsole 12 | { 13 | public class FormattingHelper 14 | { 15 | public static string GetStandardSizeString(long value) 16 | { 17 | string[] suffixes = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; 18 | int suffixIndex = 0; 19 | while (value > 9999) 20 | { 21 | value = value / 1024; 22 | suffixIndex++; 23 | } 24 | 25 | if (suffixIndex < suffixes.Length) 26 | { 27 | string FourCharacterValue = value.ToString(); 28 | while (FourCharacterValue.Length < 4) 29 | { 30 | FourCharacterValue = " " + FourCharacterValue; 31 | } 32 | return String.Format("{0} {1}", FourCharacterValue, suffixes[suffixIndex]); 33 | } 34 | else 35 | { 36 | return "> 9999 EB"; 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ISCSIConsole/Helpers/LockUtils.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using DiskAccessLibrary; 11 | using DiskAccessLibrary.Win32; 12 | 13 | namespace ISCSIConsole 14 | { 15 | public class LockUtils 16 | { 17 | public static void ReleaseDisks(List disks) 18 | { 19 | foreach (Disk disk in disks) 20 | { 21 | ReleaseDisk(disk); 22 | } 23 | } 24 | 25 | public static void ReleaseDisk(Disk disk) 26 | { 27 | if (disk is DiskImage) 28 | { 29 | ((DiskImage)disk).ReleaseLock(); 30 | } 31 | else if (disk is RAMDisk) 32 | { 33 | ((RAMDisk)disk).Free(); 34 | } 35 | else if (disk is PhysicalDisk) // Win32 only 36 | { 37 | if (!DiskAccessLibrary.LogicalDiskManager.DynamicDisk.IsDynamicDisk(disk)) 38 | { 39 | LockHelper.UnlockBasicDiskAndVolumes((PhysicalDisk)disk); 40 | try 41 | { 42 | ((PhysicalDisk)disk).UpdateProperties(); 43 | } 44 | catch (System.IO.IOException) 45 | { 46 | } 47 | } 48 | } 49 | else if (disk is VolumeDisk) // Win32 only 50 | { 51 | bool skippedLock = (Environment.OSVersion.Version.Major >= 6 && VolumeInfo.IsOffline(((VolumeDisk)disk).Volume)); 52 | if (!skippedLock) 53 | { 54 | Guid? windowsVolumeGuid = WindowsVolumeHelper.GetWindowsVolumeGuid(((VolumeDisk)disk).Volume); 55 | if (windowsVolumeGuid.HasValue) 56 | { 57 | WindowsVolumeManager.ReleaseLock(windowsVolumeGuid.Value); 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ISCSIConsole/Helpers/RuntimeHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace ISCSIConsole 4 | { 5 | public class RuntimeHelper 6 | { 7 | public static bool IsWin32 8 | { 9 | get 10 | { 11 | #if NET472 || NETCOREAPP 12 | return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); 13 | #else 14 | return true; 15 | #endif 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ISCSIConsole/Helpers/UsageCounter.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using System.Threading; 11 | 12 | namespace ISCSIConsole 13 | { 14 | public class UsageCounter 15 | { 16 | private SortedList m_targetsInUse = new SortedList(); 17 | private int m_sessionCount = 0; 18 | 19 | public void NotifySessionStart(string targetName) 20 | { 21 | Interlocked.Increment(ref m_sessionCount); 22 | lock (m_targetsInUse) 23 | { 24 | int index = m_targetsInUse.IndexOfKey(targetName); 25 | if (index >= 0) 26 | { 27 | m_targetsInUse[targetName]++; 28 | } 29 | else 30 | { 31 | m_targetsInUse.Add(targetName, 1); 32 | } 33 | } 34 | } 35 | 36 | public void NotifySessionTermination(string targetName) 37 | { 38 | Interlocked.Decrement(ref m_sessionCount); 39 | lock (m_targetsInUse) 40 | { 41 | int index = m_targetsInUse.IndexOfKey(targetName); 42 | if (index >= 0) 43 | { 44 | int useCount = m_targetsInUse[targetName]; 45 | useCount--; 46 | if (useCount == 0) 47 | { 48 | m_targetsInUse.Remove(targetName); 49 | } 50 | else 51 | { 52 | m_targetsInUse[targetName] = useCount; 53 | } 54 | } 55 | } 56 | } 57 | 58 | public bool IsTargetInUse(string targetName) 59 | { 60 | lock (m_targetsInUse) 61 | { 62 | return m_targetsInUse.ContainsKey(targetName); 63 | } 64 | } 65 | 66 | public int SessionCount 67 | { 68 | get 69 | { 70 | return m_sessionCount; 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /ISCSIConsole/ILMerge/ILMerge.bat: -------------------------------------------------------------------------------- 1 | IF ["%programfiles(x86)%"]==[""] SET ilmergePath="%programfiles%\Microsoft\ILMerge" 2 | IF NOT ["%programfiles(x86)%"]==[""] SET ilmergePath="%programfiles(x86)%\Microsoft\ILMerge" 3 | 4 | set TargetFramework=%1 5 | if %TargetFramework%==net20 set TargetPlaform=2.0 6 | if %TargetFramework%==net40 set TargetPlaform=4.0 7 | if %TargetFramework%==net472 set TargetPlaform=4.0 8 | set binaryPath=%CD%\..\bin\Release\%TargetFramework% 9 | set outputPath=%CD%\..\bin\ILMerge\%TargetFramework% 10 | IF NOT EXIST "%outputPath%" MKDIR "%outputPath%" 11 | %ilmergePath%\ilmerge /targetplatform=%TargetPlaform% /ndebug /target:winexe /out:"%outputPath%\ISCSIConsole.exe" "%binaryPath%\ISCSIConsole.exe" "%binaryPath%\Utilities.dll" "%binaryPath%\DiskAccessLibrary.dll" "%binaryPath%\DiskAccessLibrary.Win32.dll" "%binaryPath%\ISCSI.dll" -------------------------------------------------------------------------------- /ISCSIConsole/ISCSIConsole.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net20;net40;net472;netcoreapp3.1 6 | iSCSI Console 7 | iSCSI Console 8 | 1.5.5 9 | true 10 | Icons\SCSI.ico 11 | Tal Aloni 12 | Copyright © Tal Aloni 2012-2024 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ISCSIConsole/Icons/SCSI.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TalAloni/iSCSIConsole/2a75782fbfd10c73c4f26e0711fb3678d54fa1dd/ISCSIConsole/Icons/SCSI.ico -------------------------------------------------------------------------------- /ISCSIConsole/Program.Log.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.IO; 9 | using ISCSI.Logging; 10 | 11 | namespace ISCSIConsole 12 | { 13 | public partial class Program 14 | { 15 | private static FileStream m_logFile; 16 | 17 | public static bool OpenLogFile(string logFilePath) 18 | { 19 | try 20 | { 21 | // We must avoid using buffered writes, using it will negatively affect the performance and reliability. 22 | // Note: once the file system write buffer is filled, Windows may delay any (buffer-dependent) pending write operations, which will create a deadlock. 23 | m_logFile = new FileStream(logFilePath, FileMode.Append, FileAccess.Write, FileShare.Read, 0x1000, FileOptions.WriteThrough); 24 | return true; 25 | } 26 | catch 27 | { 28 | return false; 29 | } 30 | } 31 | 32 | public static void CloseLogFile() 33 | { 34 | if (m_logFile != null) 35 | { 36 | lock (m_logFile) 37 | { 38 | m_logFile.Close(); 39 | m_logFile = null; 40 | } 41 | } 42 | } 43 | 44 | public static void OnLogEntry(object sender, LogEntry entry) 45 | { 46 | if (m_logFile != null && entry.Severity != Severity.Trace) 47 | { 48 | lock (m_logFile) 49 | { 50 | StreamWriter writer = new StreamWriter(m_logFile); 51 | string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss "); 52 | writer.WriteLine("{0} {1} [{2}] {3}", entry.Severity.ToString().PadRight(12), timestamp, entry.Source, entry.Message); 53 | writer.Flush(); 54 | } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ISCSIConsole/Program.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using System.Text; 11 | using System.Threading; 12 | using System.Windows.Forms; 13 | 14 | namespace ISCSIConsole 15 | { 16 | public partial class Program 17 | { 18 | /// 19 | /// The main entry point for the application. 20 | /// 21 | [STAThread] 22 | static void Main(string[] args) 23 | { 24 | Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException); 25 | AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); 26 | 27 | if (args.Length > 0) 28 | { 29 | if (args[0] == "/help") 30 | { 31 | 32 | } 33 | if (args[0] == "/log") 34 | { 35 | string path = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location); 36 | if (!path.EndsWith(@"\")) 37 | { 38 | path += @"\"; 39 | } 40 | path += String.Format("Log {0}.txt", DateTime.Now.ToString("yyyy-MM-dd HH-mm")); 41 | bool success = OpenLogFile(path); 42 | if (!success) 43 | { 44 | MessageBox.Show("Cannot open log file", "Error"); 45 | } 46 | } 47 | else 48 | { 49 | StringBuilder builder = new StringBuilder(); 50 | builder.AppendLine("Command line arguments:"); 51 | builder.AppendLine("/log - will write log file to executable directory"); 52 | MessageBox.Show(builder.ToString(), "Error"); 53 | return; 54 | } 55 | } 56 | Application.EnableVisualStyles(); 57 | Application.SetCompatibleTextRenderingDefault(false); 58 | Application.Run(new MainForm()); 59 | } 60 | 61 | public static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) 62 | { 63 | HandleUnhandledException(e.Exception); 64 | } 65 | 66 | private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 67 | { 68 | if (e.ExceptionObject != null) 69 | { 70 | Exception ex = (Exception)e.ExceptionObject; 71 | HandleUnhandledException(ex); 72 | } 73 | } 74 | 75 | private static void HandleUnhandledException(Exception ex) 76 | { 77 | string message = String.Format("Exception: {0}: {1} Source: {2} {3}", ex.GetType(), ex.Message, ex.Source, ex.StackTrace); 78 | MessageBox.Show(message, "Error"); 79 | Application.Exit(); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /ISCSIConsole/RevisionHistory.txt: -------------------------------------------------------------------------------- 1 | Revision History: 2 | ----------------- 3 | 1.0.0 - Initial release as an independent program (previously part of Raid5Manager). 4 | 5 | 1.0.1 - Attach command now offers full control over target name. 6 | 7 | 1.0.2 - Help screens have been improved. 8 | 9 | 1.0.3 - Disabled file system caching for virtual disks. 10 | 11 | 1.0.4 - Added VHD compatibility check. 12 | Added limited VMDK support. 13 | 14 | 1.0.5 - Fixed VMDK related bug (flat files that have a space in their filename). 15 | Added support for reading a monolithic sparse VMDK. 16 | 17 | 1.0.6 - Fixed protocol related bugs. 18 | Properly handle implicit logout. 19 | Improved logging. 20 | 21 | 1.0.7 - Added support for the 'Request Sense' command. 22 | 23 | 1.0.8 - Nagle's algorithm has been disabled. 24 | Unsupported SCSI CDBs are now properly rejected. 25 | 26 | 1.0.9 - Improved support for Windows Vista and newer. 27 | 28 | 1.1.0 - Better handling of unsupported VHDs / VMDKs. 29 | 30 | 1.1.1 - Read requests outside LBA range are now returning the proper SCSI status. 31 | 32 | 1.1.2 - Improved support for multiple connections to the iSCSI server. 33 | 34 | 1.1.6 - Minor UI enhancements. 35 | 36 | 1.1.7 - Bugfix: The NOP-Out response did not have the 'Initiator Task Tag' field set. 37 | 38 | 1.1.8 - Added support for the Reserve6, Release6 SCSI commands. 39 | Added support for the Control mode page (0x0A) PageCode for the ModeSense6 SCSI command. 40 | 41 | 1.1.9 - Added support for the Read6, Write6 SCSI commands. 42 | Added support for the Block Limits (0xB0), Block Device Characteristics (0xB1) VPD pages. 43 | Unsupported SCSI commands are now properly reported to the initiator. 44 | 45 | 1.2.0 - Disk images are now locked for exclusive access. 46 | 47 | 1.2.1 - We now use noncached I/O operations when working with virtual disks. 48 | 49 | 1.2.2 - Updates to the ISCSI library. 50 | 51 | 1.2.3 - Updates to the ISCSI library. 52 | 53 | 1.2.4 - Updates to the ISCSI library. 54 | 55 | 1.2.5 - Updates to the ISCSI library. 56 | 57 | 1.2.6 - Updates to the ISCSI library. 58 | 59 | 1.2.7 - Updates to the ISCSI library. 60 | 61 | 1.2.8 - Updates to the ISCSI library. 62 | 63 | 1.2.9 - Updates to the ISCSI library. 64 | 65 | 1.3.0 - Updates to the ISCSI library. 66 | 67 | 1.3.1 - Updates to the ISCSI library. 68 | 69 | 1.3.2 - Updates to the ISCSI library. 70 | 71 | 1.3.3 - Updates to the ISCSI library. 72 | 73 | 1.3.4 - Updates to the ISCSI library. 74 | 75 | 1.3.5 - Updates to the ISCSI library. 76 | 77 | 1.3.6 - Updates to the ISCSI library. 78 | 79 | 1.3.7 - Updates to the ISCSI library. 80 | 81 | 1.3.8 - Updates to the ISCSI library. 82 | 83 | 1.3.9 - Improved logging. 84 | 85 | 1.4.0 - Replaced the Command-line interface with a GUI. 86 | 87 | 1.4.1 - Improved exception handling when opening a disk image. 88 | Added author information to the status bar. 89 | Added (hidden) option to create a RAM Disk. 90 | Bugfix: iSCSI Server did not start (when calling Start) after a SocketException. 91 | 92 | 1.4.2 - Updates to the ISCSI library. 93 | 94 | 1.4.3 - .bin files will now be listed when opening a virtual disk image. 95 | 96 | 1.4.4 - Updates to the ISCSI library. 97 | 98 | 1.4.5 - Updates to the ISCSI library. 99 | 100 | 1.4.6 - Use Control instead of Shift to show (hidden) RAM Disk creation button. 101 | 102 | 1.4.7 - Updates to the ISCSI library. 103 | 104 | 1.4.8 - Updates to the ISCSI library. 105 | 106 | 1.4.9 - Updates to the ISCSI library. 107 | 108 | 1.5.0 - Writing to a Dynamic VHD is now supported. 109 | Updates to the ISCSI library. 110 | 111 | 1.5.1 - Bugfix: 'Read only' checkbox was ignored. 112 | 113 | 1.5.2 - ISCSIClient: Fixed a bug related to localhost communication. 114 | ISCSIConsole: Add .NET Framework 4.7.2 target. 115 | Create Disk Image form: Updated tab index. 116 | 117 | 1.5.3 - StandardInquiryData: Fix bug in GetBytes not reporting PeripheralDeviceType correctly. 118 | Updated DiskAccessLibrary to v1.6.1 (VMDK related improvements). -------------------------------------------------------------------------------- /ISCSIConsole/SelectDiskImageForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Drawing; 5 | using System.IO; 6 | using System.Text; 7 | using System.Windows.Forms; 8 | using DiskAccessLibrary; 9 | 10 | namespace ISCSIConsole 11 | { 12 | public partial class SelectDiskImageForm : Form 13 | { 14 | private DiskImage m_diskImage; 15 | 16 | public SelectDiskImageForm() 17 | { 18 | InitializeComponent(); 19 | } 20 | 21 | private void btnCancel_Click(object sender, EventArgs e) 22 | { 23 | this.DialogResult = DialogResult.Cancel; 24 | this.Close(); 25 | } 26 | 27 | private void btnOK_Click(object sender, EventArgs e) 28 | { 29 | string path = txtFilePath.Text; 30 | if (path == String.Empty) 31 | { 32 | MessageBox.Show("Please choose file location.", "Error"); 33 | return; 34 | } 35 | DiskImage diskImage; 36 | try 37 | { 38 | diskImage = DiskImage.GetDiskImage(path, chkReadOnly.Checked); 39 | } 40 | catch (IOException ex) 41 | { 42 | MessageBox.Show("Can't open disk image: " + ex.Message, "Error"); 43 | return; 44 | } 45 | catch (InvalidDataException ex) 46 | { 47 | MessageBox.Show("Invalid disk image: " + ex.Message, "Error"); 48 | return; 49 | } 50 | catch (NotImplementedException) 51 | { 52 | MessageBox.Show("Unsupported Disk Image Format", "Error"); 53 | return; 54 | } 55 | 56 | bool isLocked = false; 57 | try 58 | { 59 | isLocked = diskImage.ExclusiveLock(); 60 | } 61 | catch (IOException) 62 | { 63 | } 64 | if (!isLocked) 65 | { 66 | MessageBox.Show("Cannot lock the disk image for exclusive access.", "Error"); 67 | return; 68 | } 69 | m_diskImage = diskImage; 70 | this.DialogResult = DialogResult.OK; 71 | this.Close(); 72 | } 73 | 74 | private void btnBrowse_Click(object sender, EventArgs e) 75 | { 76 | DialogResult result = openDiskImageDialog.ShowDialog(); 77 | if (result == DialogResult.OK) 78 | { 79 | txtFilePath.Text = openDiskImageDialog.FileName; 80 | } 81 | } 82 | 83 | public DiskImage DiskImage 84 | { 85 | get 86 | { 87 | return m_diskImage; 88 | } 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /ISCSIConsole/Win32/SecurityHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Security.Principal; 10 | 11 | namespace ISCSIConsole 12 | { 13 | public class SecurityHelper 14 | { 15 | public static bool IsAdministrator() 16 | { 17 | WindowsIdentity windowsIdentity = null; 18 | try 19 | { 20 | windowsIdentity = WindowsIdentity.GetCurrent(); 21 | } 22 | catch 23 | { 24 | return false; 25 | } 26 | WindowsPrincipal windowsPrincipal = new WindowsPrincipal(windowsIdentity); 27 | return windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ISCSIConsole/Win32/VolumeInfo.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using DiskAccessLibrary; 11 | using DiskAccessLibrary.LogicalDiskManager; 12 | using DiskAccessLibrary.Win32; 13 | 14 | namespace ISCSIConsole 15 | { 16 | public class VolumeInfo 17 | { 18 | public static bool IsOffline(Volume volume) 19 | { 20 | IList disks = GetVolumeDisks(volume); 21 | foreach (PhysicalDisk disk in disks) 22 | { 23 | bool isOnline = disk.GetOnlineStatus(); 24 | if (isOnline) 25 | { 26 | return false; 27 | } 28 | } 29 | return true; 30 | } 31 | 32 | private static IList GetVolumeDisks(Volume volume) 33 | { 34 | SortedList disks = new SortedList(); 35 | if (volume is DynamicVolume) 36 | { 37 | foreach (DiskExtent extent in ((DynamicVolume)volume).Extents) 38 | { 39 | if (extent.Disk is PhysicalDisk) 40 | { 41 | PhysicalDisk disk = (PhysicalDisk)extent.Disk; 42 | if (!disks.ContainsKey(disk.PhysicalDiskIndex)) 43 | { 44 | disks.Add(disk.PhysicalDiskIndex, disk); 45 | } 46 | } 47 | } 48 | } 49 | else if (volume is Partition) 50 | { 51 | Partition partition = (Partition)volume; 52 | if (partition.Disk is PhysicalDisk) 53 | { 54 | PhysicalDisk disk = (PhysicalDisk)partition.Disk; 55 | disks.Add(disk.PhysicalDiskIndex, (PhysicalDisk)disk); 56 | } 57 | } 58 | return disks.Values; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ISCSIConsole_UI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TalAloni/iSCSIConsole/2a75782fbfd10c73c4f26e0711fb3678d54fa1dd/ISCSIConsole_UI.png -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | About iSCSI Console: 2 | ==================== 3 | iSCSI Console is a Free, Open Source, User-Mode iSCSI Target Server written in C#. 4 | iSCSI Console is cross-platform, portable and requires no installation. 5 | iSCSI Console can serve physical and virtual disks to multiple clients. 6 | 7 | About the iSCSI library: 8 | ======================== 9 | The iSCSI library utilized by iSCSI Console was designed to give developers an easy way to serve block storage via iSCSI. 10 | Any storage object you wish to share needs to implement the abstract Disk class, and the library will take care of the rest. 11 | The library was written with extensibility in mind, and was designed to fit multitude of projects. 12 | iSCSI Console is merely a demo project that exposes some of the capabilities of this library. 13 | 14 | A NuGet package of the library [is available](https://www.nuget.org/packages/ISCSI/). 15 | 16 | Notes: 17 | ------ 18 | In addition to a full fledged iSCSI Target server implementation, the iSCSI library also contain a very basic iSCSI initiator implementation. 19 | 20 | What this program can do: 21 | =================================== 22 | 1. Serve virtual disks (VHD / VMDK / IMG). 23 | 2. Serve physical disks. 24 | 3. Serve basic volumes as disks. 25 | 4. Serve dynamic volumes as disks. 26 | 5. Create VHDs. 27 | 6. Can run under Windows PE using Mono. 28 | 7. Can run under Linux / OSX using Mono (use the release targeting .NET Framework 4.7.2) 29 | 30 | ![iSCSI Console UI](ISCSIConsole_UI.png) 31 | 32 | Contact: 33 | ======== 34 | If you have any question, feel free to contact me. 35 | Tal Aloni 36 | -------------------------------------------------------------------------------- /Utilities/ByteUtils/ByteUtils.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.IO; 9 | 10 | namespace Utilities 11 | { 12 | public class ByteUtils 13 | { 14 | public static byte[] Concatenate(byte[] a, byte[] b) 15 | { 16 | byte[] result = new byte[a.Length + b.Length]; 17 | Array.Copy(a, 0, result, 0, a.Length); 18 | Array.Copy(b, 0, result, a.Length, b.Length); 19 | return result; 20 | } 21 | 22 | public static bool AreByteArraysEqual(byte[] array1, byte[] array2) 23 | { 24 | if (array1.Length != array2.Length) 25 | { 26 | return false; 27 | } 28 | 29 | for (int index = 0; index < array1.Length; index++) 30 | { 31 | if (array1[index] != array2[index]) 32 | { 33 | return false; 34 | } 35 | } 36 | 37 | return true; 38 | } 39 | 40 | public static byte[] XOR(byte[] array1, byte[] array2) 41 | { 42 | if (array1.Length == array2.Length) 43 | { 44 | return XOR(array1, 0, array2, 0, array1.Length); 45 | } 46 | else 47 | { 48 | throw new ArgumentException("Arrays must be of equal length"); 49 | } 50 | } 51 | 52 | public static byte[] XOR(byte[] array1, int offset1, byte[] array2, int offset2, int length) 53 | { 54 | if (offset1 + length <= array1.Length && offset2 + length <= array2.Length) 55 | { 56 | byte[] result = new byte[length]; 57 | for (int index = 0; index < length; index++) 58 | { 59 | result[index] = (byte)(array1[offset1 + index] ^ array2[offset2 + index]); 60 | } 61 | return result; 62 | } 63 | else 64 | { 65 | throw new ArgumentOutOfRangeException(); 66 | } 67 | } 68 | 69 | public static long CopyStream(Stream input, Stream output) 70 | { 71 | // input may not support seeking, so don't use input.Position 72 | return CopyStream(input, output, Int64.MaxValue); 73 | } 74 | 75 | public static long CopyStream(Stream input, Stream output, long count) 76 | { 77 | const int MaxBufferSize = 1048576; // 1 MB 78 | int bufferSize = (int)Math.Min(MaxBufferSize, count); 79 | byte[] buffer = new byte[bufferSize]; 80 | long totalBytesRead = 0; 81 | while (totalBytesRead < count) 82 | { 83 | int numberOfBytesToRead = (int)Math.Min(bufferSize, count - totalBytesRead); 84 | int bytesRead = input.Read(buffer, 0, numberOfBytesToRead); 85 | totalBytesRead += bytesRead; 86 | output.Write(buffer, 0, bytesRead); 87 | if (bytesRead == 0) // no more bytes to read from input stream 88 | { 89 | return totalBytesRead; 90 | } 91 | } 92 | return totalBytesRead; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Utilities/ByteUtils/LittleEndianReader.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.IO; 9 | 10 | namespace Utilities 11 | { 12 | public class LittleEndianReader 13 | { 14 | public static short ReadInt16(byte[] buffer, ref int offset) 15 | { 16 | offset += 2; 17 | return LittleEndianConverter.ToInt16(buffer, offset - 2); 18 | } 19 | 20 | public static ushort ReadUInt16(byte[] buffer, ref int offset) 21 | { 22 | offset += 2; 23 | return LittleEndianConverter.ToUInt16(buffer, offset - 2); 24 | } 25 | 26 | public static int ReadInt32(byte[] buffer, ref int offset) 27 | { 28 | offset += 4; 29 | return LittleEndianConverter.ToInt32(buffer, offset - 4); 30 | } 31 | 32 | public static uint ReadUInt32(byte[] buffer, ref int offset) 33 | { 34 | offset += 4; 35 | return LittleEndianConverter.ToUInt32(buffer, offset - 4); 36 | } 37 | 38 | public static long ReadInt64(byte[] buffer, ref int offset) 39 | { 40 | offset += 8; 41 | return LittleEndianConverter.ToInt64(buffer, offset - 8); 42 | } 43 | 44 | public static ulong ReadUInt64(byte[] buffer, ref int offset) 45 | { 46 | offset += 8; 47 | return LittleEndianConverter.ToUInt64(buffer, offset - 8); 48 | } 49 | 50 | public static Guid ReadGuid(byte[] buffer, ref int offset) 51 | { 52 | offset += 16; 53 | return LittleEndianConverter.ToGuid(buffer, offset - 16); 54 | } 55 | 56 | public static short ReadInt16(Stream stream) 57 | { 58 | byte[] buffer = new byte[2]; 59 | stream.Read(buffer, 0, 2); 60 | return LittleEndianConverter.ToInt16(buffer, 0); 61 | } 62 | 63 | public static ushort ReadUInt16(Stream stream) 64 | { 65 | byte[] buffer = new byte[2]; 66 | stream.Read(buffer, 0, 2); 67 | return LittleEndianConverter.ToUInt16(buffer, 0); 68 | } 69 | 70 | public static int ReadInt32(Stream stream) 71 | { 72 | byte[] buffer = new byte[4]; 73 | stream.Read(buffer, 0, 4); 74 | return LittleEndianConverter.ToInt32(buffer, 0); 75 | } 76 | 77 | public static uint ReadUInt32(Stream stream) 78 | { 79 | byte[] buffer = new byte[4]; 80 | stream.Read(buffer, 0, 4); 81 | return LittleEndianConverter.ToUInt32(buffer, 0); 82 | } 83 | 84 | public static long ReadInt64(Stream stream) 85 | { 86 | byte[] buffer = new byte[8]; 87 | stream.Read(buffer, 0, 8); 88 | return LittleEndianConverter.ToInt64(buffer, 0); 89 | } 90 | 91 | public static ulong ReadUInt64(Stream stream) 92 | { 93 | byte[] buffer = new byte[8]; 94 | stream.Read(buffer, 0, 8); 95 | return LittleEndianConverter.ToUInt64(buffer, 0); 96 | } 97 | 98 | public static Guid ReadGuid(Stream stream) 99 | { 100 | byte[] buffer = new byte[16]; 101 | stream.Read(buffer, 0, 16); 102 | return LittleEndianConverter.ToGuid(buffer, 0); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Utilities/Comparers/ReverseComparer.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2005-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace Utilities 11 | { 12 | public class ReverseComparer : IComparer 13 | { 14 | private IComparer m_comparer; 15 | 16 | public ReverseComparer(IComparer comparer) 17 | { 18 | m_comparer = comparer; 19 | } 20 | 21 | public int Compare(T x, T y) 22 | { 23 | return m_comparer.Compare(y, x); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Utilities/Cryptography/AesCmac.cs: -------------------------------------------------------------------------------- 1 | /* Based on https://stackoverflow.com/a/30123190/3419770 2 | */ 3 | using System; 4 | using System.IO; 5 | using System.Security.Cryptography; 6 | 7 | namespace Utilities 8 | { 9 | public static class AesCmac 10 | { 11 | public static byte[] CalculateAesCmac(byte[] key, byte[] buffer, int offset, int length) 12 | { 13 | byte[] data = ByteReader.ReadBytes(buffer, offset, length); 14 | return CalculateAesCmac(key, data); 15 | } 16 | 17 | public static byte[] CalculateAesCmac(byte[] key, byte[] data) 18 | { 19 | // SubKey generation 20 | // step 1, AES-128 with key K is applied to an all-zero input block. 21 | byte[] L = AESEncrypt(key, new byte[16], new byte[16]); 22 | 23 | // step 2, K1 is derived through the following operation: 24 | byte[] FirstSubkey = Rol(L); //If the most significant bit of L is equal to 0, K1 is the left-shift of L by 1 bit. 25 | if ((L[0] & 0x80) == 0x80) 26 | FirstSubkey[15] ^= 0x87; // Otherwise, K1 is the exclusive-OR of const_Rb and the left-shift of L by 1 bit. 27 | 28 | // step 3, K2 is derived through the following operation: 29 | byte[] SecondSubkey = Rol(FirstSubkey); // If the most significant bit of K1 is equal to 0, K2 is the left-shift of K1 by 1 bit. 30 | if ((FirstSubkey[0] & 0x80) == 0x80) 31 | SecondSubkey[15] ^= 0x87; // Otherwise, K2 is the exclusive-OR of const_Rb and the left-shift of K1 by 1 bit. 32 | 33 | // MAC computing 34 | if (((data.Length != 0) && (data.Length % 16 == 0)) == true) 35 | { 36 | // If the size of the input message block is equal to a positive multiple of the block size (namely, 128 bits), 37 | // the last block shall be exclusive-OR'ed with K1 before processing 38 | for (int j = 0; j < FirstSubkey.Length; j++) 39 | data[data.Length - 16 + j] ^= FirstSubkey[j]; 40 | } 41 | else 42 | { 43 | // Otherwise, the last block shall be padded with 10^i 44 | byte[] padding = new byte[16 - data.Length % 16]; 45 | padding[0] = 0x80; 46 | 47 | data = ByteUtils.Concatenate(data, padding); 48 | 49 | // and exclusive-OR'ed with K2 50 | for (int j = 0; j < SecondSubkey.Length; j++) 51 | data[data.Length - 16 + j] ^= SecondSubkey[j]; 52 | } 53 | 54 | // The result of the previous process will be the input of the last encryption. 55 | byte[] encResult = AESEncrypt(key, new byte[16], data); 56 | 57 | byte[] HashValue = new byte[16]; 58 | Array.Copy(encResult, encResult.Length - HashValue.Length, HashValue, 0, HashValue.Length); 59 | 60 | return HashValue; 61 | } 62 | 63 | private static byte[] AESEncrypt(byte[] key, byte[] iv, byte[] data) 64 | { 65 | using (MemoryStream ms = new MemoryStream()) 66 | { 67 | RijndaelManaged aes = new RijndaelManaged(); 68 | aes.Mode = CipherMode.CBC; 69 | aes.Padding = PaddingMode.None; 70 | 71 | using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(key, iv), CryptoStreamMode.Write)) 72 | { 73 | cs.Write(data, 0, data.Length); 74 | cs.FlushFinalBlock(); 75 | 76 | return ms.ToArray(); 77 | } 78 | } 79 | } 80 | 81 | private static byte[] Rol(byte[] b) 82 | { 83 | byte[] r = new byte[b.Length]; 84 | byte carry = 0; 85 | 86 | for (int i = b.Length - 1; i >= 0; i--) 87 | { 88 | ushort u = (ushort)(b[i] << 1); 89 | r[i] = (byte)((u & 0xff) + carry); 90 | carry = (byte)((u & 0xff00) >> 8); 91 | } 92 | 93 | return r; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Utilities/Generics/BlockingQueue.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016-2025 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | using System.Threading; 9 | 10 | namespace Utilities 11 | { 12 | public class BlockingQueue 13 | { 14 | private Queue m_queue = new Queue(); 15 | private int m_count = 0; 16 | private bool m_stopping; 17 | 18 | public void Enqueue(T item) 19 | { 20 | if (m_stopping) 21 | { 22 | return; 23 | } 24 | 25 | lock (m_queue) 26 | { 27 | m_queue.Enqueue(item); 28 | m_count++; 29 | if (m_queue.Count == 1) 30 | { 31 | Monitor.Pulse(m_queue); 32 | } 33 | } 34 | } 35 | 36 | public void Enqueue(List items) 37 | { 38 | if (m_stopping || items.Count == 0) 39 | { 40 | return; 41 | } 42 | 43 | lock (m_queue) 44 | { 45 | foreach (T item in items) 46 | { 47 | m_queue.Enqueue(item); 48 | m_count++; 49 | } 50 | if (m_queue.Count == items.Count) 51 | { 52 | Monitor.Pulse(m_queue); 53 | } 54 | } 55 | } 56 | 57 | /// Will return false if the BlockingQueue is stopped 58 | public bool TryDequeue(out T item) 59 | { 60 | lock (m_queue) 61 | { 62 | while (m_queue.Count == 0) 63 | { 64 | if (!m_stopping) 65 | { 66 | Monitor.Wait(m_queue); 67 | } 68 | 69 | if (m_stopping && m_queue.Count == 0) 70 | { 71 | item = default(T); 72 | return false; 73 | } 74 | } 75 | 76 | item = m_queue.Dequeue(); 77 | m_count--; 78 | return true; 79 | } 80 | } 81 | 82 | public void Stop() 83 | { 84 | lock (m_queue) 85 | { 86 | m_stopping = true; 87 | Monitor.PulseAll(m_queue); 88 | } 89 | } 90 | 91 | public void Abort() 92 | { 93 | lock (m_queue) 94 | { 95 | m_queue.Clear(); 96 | Stop(); 97 | } 98 | } 99 | 100 | public int Count 101 | { 102 | get 103 | { 104 | return m_count; 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Utilities/Generics/KeyValuePairList.Sort.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | using System.ComponentModel; 9 | 10 | namespace Utilities 11 | { 12 | public partial class KeyValuePairList 13 | { 14 | new public void Sort() 15 | { 16 | this.Sort(Comparer.Default); 17 | } 18 | 19 | public void Sort(ListSortDirection sortDirection) 20 | { 21 | Sort(Comparer.Default, sortDirection); 22 | } 23 | 24 | public void Sort(IComparer comparer, ListSortDirection sortDirection) 25 | { 26 | if (sortDirection == ListSortDirection.Ascending) 27 | { 28 | Sort(comparer); 29 | } 30 | else 31 | { 32 | Sort(new ReverseComparer(comparer)); 33 | } 34 | } 35 | 36 | public void Sort(IComparer comparer) 37 | { 38 | this.Sort(delegate(KeyValuePair a, KeyValuePair b) 39 | { 40 | return comparer.Compare(a.Key, b.Key); 41 | }); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Utilities/Generics/KeyValuePairList.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System.Collections.Generic; 8 | 9 | namespace Utilities 10 | { 11 | public partial class KeyValuePairList : List> 12 | { 13 | public KeyValuePairList() 14 | { 15 | } 16 | 17 | public KeyValuePairList(List> collection) : base(collection) 18 | { 19 | } 20 | 21 | public bool ContainsKey(TKey key) 22 | { 23 | return (this.IndexOfKey(key) != -1); 24 | } 25 | 26 | public int IndexOfKey(TKey key) 27 | { 28 | for (int index = 0; index < this.Count; index++) 29 | { 30 | if (this[index].Key.Equals(key)) 31 | { 32 | return index; 33 | } 34 | } 35 | 36 | return -1; 37 | } 38 | 39 | public TValue ValueOf(TKey key) 40 | { 41 | for (int index = 0; index < this.Count; index++) 42 | { 43 | if (this[index].Key.Equals(key)) 44 | { 45 | return this[index].Value; 46 | } 47 | } 48 | 49 | return default(TValue); 50 | } 51 | 52 | public void Add(TKey key, TValue value) 53 | { 54 | this.Add(new KeyValuePair(key, value)); 55 | } 56 | 57 | public List Keys 58 | { 59 | get 60 | { 61 | List result = new List(); 62 | foreach (KeyValuePair entity in this) 63 | { 64 | result.Add(entity.Key); 65 | } 66 | return result; 67 | } 68 | } 69 | 70 | public List Values 71 | { 72 | get 73 | { 74 | List result = new List(); 75 | foreach (KeyValuePair entity in this) 76 | { 77 | result.Add(entity.Value); 78 | } 79 | return result; 80 | } 81 | } 82 | 83 | public new KeyValuePairList GetRange(int index, int count) 84 | { 85 | return new KeyValuePairList(base.GetRange(index, count)); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Utilities/Generics/Map.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2014-2020 Tal Aloni . All rights reserved. 2 | * 3 | * Based on: 4 | * http://stackoverflow.com/questions/10966331/two-way-bidirectional-dictionary-in-c 5 | * 6 | * You can redistribute this program and/or modify it under the terms of 7 | * the GNU Lesser Public License as published by the Free Software Foundation, 8 | * either version 3 of the License, or (at your option) any later version. 9 | */ 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Text; 13 | 14 | namespace Utilities 15 | { 16 | public class Map 17 | { 18 | private Dictionary m_forward = new Dictionary(); 19 | private Dictionary m_reverse = new Dictionary(); 20 | 21 | public Map() 22 | { 23 | m_forward = new Dictionary(); 24 | m_reverse = new Dictionary(); 25 | } 26 | 27 | public void Add(T1 key, T2 value) 28 | { 29 | m_forward.Add(key, value); 30 | m_reverse.Add(value, key); 31 | } 32 | 33 | public bool ContainsKey(T1 key) 34 | { 35 | return m_forward.ContainsKey(key); 36 | } 37 | 38 | public bool ContainsValue(T2 value) 39 | { 40 | return m_reverse.ContainsKey(value); 41 | } 42 | 43 | public bool TryGetKey(T2 value, out T1 key) 44 | { 45 | return m_reverse.TryGetValue(value, out key); 46 | } 47 | 48 | public bool TryGetValue(T1 key, out T2 value) 49 | { 50 | return m_forward.TryGetValue(key, out value); 51 | } 52 | 53 | public void RemoveKey(T1 key) 54 | { 55 | T2 value; 56 | if (m_forward.TryGetValue(key, out value)) 57 | { 58 | m_forward.Remove(key); 59 | m_reverse.Remove(value); 60 | } 61 | } 62 | 63 | public void RemoveValue(T2 value) 64 | { 65 | T1 key; 66 | if (m_reverse.TryGetValue(value, out key)) 67 | { 68 | m_forward.Remove(key); 69 | m_reverse.Remove(value); 70 | } 71 | } 72 | 73 | public T2 this[T1 key] 74 | { 75 | get 76 | { 77 | return m_forward[key]; 78 | } 79 | } 80 | 81 | public T1 GetKey(T2 value) 82 | { 83 | return m_reverse[value]; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Utilities/Generics/Reference.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace Utilities 11 | { 12 | public class Reference where T : struct 13 | { 14 | T m_value; 15 | 16 | public Reference(T value) 17 | { 18 | m_value = value; 19 | } 20 | 21 | public T Value 22 | { 23 | get { return m_value; } 24 | set { m_value = value; } 25 | } 26 | 27 | public override string ToString() 28 | { 29 | return m_value.ToString(); 30 | } 31 | 32 | public static implicit operator T(Reference wrapper) 33 | { 34 | return wrapper.Value; 35 | } 36 | 37 | public static implicit operator Reference(T value) 38 | { 39 | return new Reference(value); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Utilities/Threading/CountdownLatch.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016-2020 Tal Aloni . All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | */ 7 | using System; 8 | using System.Threading; 9 | 10 | namespace Utilities 11 | { 12 | public class CountdownLatch 13 | { 14 | private int m_count; 15 | private EventWaitHandle m_waitHandle = new EventWaitHandle(true, EventResetMode.ManualReset); 16 | 17 | public CountdownLatch() 18 | { 19 | } 20 | 21 | public void Increment() 22 | { 23 | int count = Interlocked.Increment(ref m_count); 24 | if (count == 1) 25 | { 26 | m_waitHandle.Reset(); 27 | } 28 | } 29 | 30 | public void Add(int value) 31 | { 32 | int count = Interlocked.Add(ref m_count, value); 33 | if (count == value) 34 | { 35 | m_waitHandle.Reset(); 36 | } 37 | } 38 | 39 | public void Decrement() 40 | { 41 | int count = Interlocked.Decrement(ref m_count); 42 | if (m_count == 0) 43 | { 44 | m_waitHandle.Set(); 45 | } 46 | else if (count < 0) 47 | { 48 | throw new InvalidOperationException("Count must be greater than or equal to 0"); 49 | } 50 | } 51 | 52 | public void WaitUntilZero() 53 | { 54 | m_waitHandle.WaitOne(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Utilities/Utilities.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net20;net40;netstandard2.0 5 | false 6 | Utilities 7 | Utilities 8 | 9 | 10 | 11 | --------------------------------------------------------------------------------